Code to run analysis and generate figures for McNeall et al. (2023) “Constraining the carbon cycle in JULES-ES-1.0”


source("JULES-ES-1p0-common-packages.R")
Loading required package: spam
Loading required package: dotCall64
Loading required package: grid
Spam version 2.7-0 (2021-06-25) is loaded.
Type 'help( Spam)' or 'demo( spam)' for a short introduction 
and overview of this package.
Help for individual functions is also obtained by adding the
suffix '.spam' to the function name, e.g. 'help( chol.spam)'.

Attaching package: ‘spam’

The following objects are masked from ‘package:base’:

    backsolve, forwardsolve

Loading required package: viridis
Loading required package: viridisLite
See https://github.com/NCAR/Fields for
 an extensive vignette, other supplements and source code 
Loading required package: lhs
Loading required package: parallel
Loading required package: viztools
source("JULES-ES-1p0-common-functions.R")
source("JULES-ES-1p0-common-data.R")

0.1 What are the data?

getStandardMemberLongName <- function(ensloc, variable){
  # Helper function to get the long name of output variables (and the units)
  
  ensmember <- 'S3'
  fn <- paste0(ensloc,'JULES-ES-1p0_',ensmember,'_Annual_global.nc')

  nc <- nc_open(paste0(fn))
  ln <- nc$var[[variable]]$longname
  un <- nc$var[[variable]]$units      
  
  return(list(longname = ln, units = un))
  
}

# for(i in y_names_select){
#   
#   out <- getStandardMemberLongName(ensloc_wave00, i)
#   print(i)
#   print(out)
# }

0.2 Failure analysis



low_npp_ix <- which(Y[,'npp_nlim_lnd_sum'] < 1e5)
# code from https://stackoverflow.com/questions/28182872/how-to-use-different-sets-of-data-in-lower-and-upper-panel-of-pairs-function-in


#X <- matrix(runif(300), ncol=3)
#Y <- matrix(c(sort(runif(100, 0, 10)), 
#              sort(runif(100, 0, 10)), 
#              sort(runif(100, 0, 10))), ncol=3)

#pdf(file = 'figs/fig02.pdf', width = 12, height = 10)
#pdf(file = 'figs/run-failure-pairs.pdf', width = 12, height = 10)
x1 <- X[low_npp_ix, ]
x2 <- X_nlevel0

XY <- rbind(x1, x2)


pairs(XY,
      lower.panel=function(x, y, ...) {
        Xx <- x[seq_len(nrow(x1))] # corresponds to X subset
        Xy <- y[seq_len(nrow(x1))] # corresponds to X subset
        #usr <- par("usr"); on.exit(par(usr))
        #par(usr = c(range(x1[, -ncol(x1)]), range(x1[, -1]))) # set up limits
        points(Xx, Xy, col = zblue, pch = 19, cex = 0.8)
       # if(par('mfg')[2] == 1) axis(2) # if left plot, add left axis
        #if(par('mfg')[1] == ncol(x1)) axis(1) # if bottom plot add bottom axis
      }, 
      upper.panel=function(x, y, ...) {
        Yx <- x[(nrow(x1) + 1):length(x)] # Y subset
        Yy <- y[(nrow(x1) + 1):length(y)] # Y subset
        
        #cntr <- outer(Yx, Yx, FUN='*') # arbitrary function for contour
       # usr <- par("usr"); on.exit(par(usr))
        #par(usr = c(range(x2[, -1]), range(x2[, -ncol(x2)]))) # set up limits
        points(Yx, Yy, col = zred, pch = 19, cex = 0.8)
        #contour(Yx, Yy, cntr, add=TRUE)
        #if(par('mfg')[2] == ncol(x2)) axis(4) # if right plot, add right axis
        #if(par('mfg')[1] == 1) axis(3) # if top plot, add top axis
      }, 
      #tick=FALSE, # suppress the default tick marks
      #line=1,
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      labels = 1:d,
      oma = c(2, 18, 2, 2)) # move the default tick labels off the plot 

reset()

legend('left', legend = paste(1:d, colnames(lhs)), cex = 1.1, bty = 'n')
legend('topleft', pch = 19, col = c( zred, zblue), legend = c('failed', 'zero carbon cycle'), bty = 'n', inset = 0.02, cex = 1.1 )


#dev.off()

0.3 Failure analysis marginal plots

Are there hard thresholds after which the model always fails? (Question from review comments). Run failures are in t



y_level0 <- Y_level0[,'npp_nlim_lnd_sum']

pdf(file = 'figs/marginal_failures.pdf', width = 10, height = 10)
par(mfrow = c(5,7), mar = c(3,1,3,1), oma = c(1,1,5,1))
for(i in 1:p){
  plot(X_level0[,i], y_level0, xlab = '', ylab = '', main = colnames(X_level0)[i], xlim = c(0,1))
  points(X_nlevel0[,i], rep(0, nrow(X_nlevel0)), col = zred, pch = 19)
  points(x1[, i ], rep(0,nrow(x1)), col = zblue, pch = 19)
  
}
mtext('NPP', outer = TRUE, side = 3, cex = 2, line = 2)
reset()
legend('bottomright', pch = 19, col = c('black', zred, zblue), legend = c('ran','failed', 'zero carbon cycle'), bty = 'n', inset = 0.05, cex = 1.1 )
dev.off()
null device 
          1 

## Is there a pattern to the wave01 timeseries outliers?


pdf(file = 'figs/X_wave01_timeseries_outliers.pdf', width = 12, height = 12)
pairs(X_wave01_train[ts_outliers_ix_wave01, ],
      gap = 0, 
      xlim = c(0,1),
      ylim = c(0,1)
)
dev.off()
null device 
          1 
      
      

1 Visualising the ensemble range

It’s important to remember that the design of the experiment is multiplication factors of the original parameters. This might be important for the “hold” value in a sensitivity analysis, as the “standard” value and the median value of the ensemble will not be the same.


#pdf(file = 'figs/lhs_range.pdf', width = 6, height = 8)
#pdf(file = 'figs/fig01.pdf', width = 6, height = 8)
par(las = 1, mar = c(5,8,2,1))
lhs_min <- apply(lhs_wave0_wave01_all, 2, min)
lhs_max <- apply(lhs_wave0_wave01_all,2, max)

plot(lhs_max, 1:d, type = 'n', xlim = c(0,10), axes = FALSE, xlab = 'multiplying factor', ylab = '')

abline(v = 0, lty = 'dashed', col = 'grey')
abline(v = 1, lty = 'dashed', col = 'tomato2')

segments(x0 = lhs_min, y0 = 1:d, x1 = lhs_max, y1 = 1:d )
points(lhs_min, 1:d, pch = 20)
points(lhs_max, 1:d, pch = 20)
axis(2, at = 1:d, labels = colnames(lhs))
axis(1)

#dev.off()

1.1 Wave00/Wave01 Ensemble behaviour in key (constraining) outputs.

Global mean for the 20 years at the end of the 20th Century. There is still a significant low bias on cVeg output.

wave00col <- 'skyblue2'
wave01col <- 'tomato2'

wave00col <- 'dodgerblue2'
wave01col <- 'firebrick'
rangecol <- 'grey'
# Histogram of level 1 constraints
hcol = 'darkgrey'
lcol = 'black'

#pdf(file = 'figs/fig05.pdf', width = 8, height = 8)
#pdf(file = 'figs/level_2_constraints_hists.pdf', width = 8, height = 8)
par(mfrow = c(2,2), fg = 'darkgrey', las = 1, oma = c(0.1, 0.1, 4, 0.1))

trunc <- function(x, vec){
  
  dat <- x[x < max(vec) & x > min(vec)  ]
  
  dat
  
}


h <- hist(Y_const_level1a_scaled[,'nbp_lnd_sum'], main = 'NBP', xlab = 'GtC/year', col = makeTransparent(wave00col,150))
hist(trunc(Y_const_wave01_scaled [,'nbp_lnd_sum'], h$breaks) ,
     col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['nbp_lnd_sum'], lwd = 2)

polygon(x = c(0, 100, 100, 0), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))

h <- hist(Y_const_level1a_scaled[,'npp_nlim_lnd_sum'],col = makeTransparent(wave00col,150), main = 'NPP', xlab = 'GtC/year')
hist(trunc(Y_const_wave01_scaled [,'npp_nlim_lnd_sum'], h$breaks) , 
     col = makeTransparent(wave01col) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['npp_nlim_lnd_sum'], lwd = 2)

polygon(x = c(35, 80, 80, 35), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))


h <- hist(Y_const_level1a_scaled[,'cSoil_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Soil Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cSoil_lnd_sum'], h$breaks) , 
     col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['cSoil_lnd_sum'], lwd = 2)

polygon(x = c(750, 3000, 3000, 750), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))

h <- hist(Y_const_level1a_scaled[,'cVeg_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Vegetation Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cVeg_lnd_sum'], h$breaks) , 
   col = makeTransparent(wave01col,150)  , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['cVeg_lnd_sum'], lwd = 2)

polygon(x = c(300, 800, 800, 300), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
       border =  makeTransparent(rangecol))



reset()

legend('top', horiz = TRUE, fill = c(makeTransparent(wave00col, 150), makeTransparent(wave01col, 150), makeTransparent(rangecol, 60)), legend = c('Wave00', 'Wave01', 'AW range'))


#dev.off()

1.2 What proportion of wave01 fall within Andy Wiltshire’s constraints?

Just under a third. Points at a significant model discrepency in cVeg

Of the 400 members of the wave01 ensemble, 128 pass Andy Wiltshire’s Level 2 constraints.


length(level2_ix_wave01)
[1] 128
length(level2_ix_wave01) / ntrain_wave01
[1] 0.32

lcol_wave0 <- makeTransparent('dodgerblue2',  120)
lcol_wave01 <- makeTransparent('firebrick',  120)
lcol_wave01_level2 <- 'gold'
stancol = 'black'

linePlotMultiEns <- function(years, ens1, ens2, ens3, col1, col2, col3, ylab, main, ylim = NULL, ...){
  # Plot wave00 and wave01 timeseries on top of one another
  
  nt <- length(years) 
  if(is.null(ylim)){
    
  ylim = range(c(ens1[,1], ens1[,nt], ens2[,1], ens2[ ,nt], ens3[,1], ens3[, nt]))
  }
  
  else ylim <- ylim
  
  matplot(years, t(ens1), type = 'l', lty = 'solid',ylim = ylim, col = col1,
        ylab = ylab, main = main, xlab = '',
        bty = 'n', ...)
  matlines(years, t(ens2), col = col2, lty = 'solid')
    matlines(years, t(ens3), col = col3, lty = 'solid')
}

pdf(file = 'figs/fig03.pdf', width = 10, height = 12)
#pdf(file = 'figs/carbon-cycle-timeseries-waves-constrained.pdf', width = 10, height = 12)
par(mfrow= c(3,5), las = 1, mar = c(4,4,1,0))

linePlotMultiEns(years = years, ens1 = npp_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = npp_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = npp_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NPP')

lines(years,npp_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 =  nbp_ens_wave00[without_outliers_ix_wave00,], 
                 ens2 = nbp_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = nbp_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NBP', ylim = c(-10,10))

lines(years, nbp_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cSoil_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = cSoil_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = cSoil_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cSoil', ylim = range(c(cSoil_ens_wave00[,1], cSoil_ens_wave00[,164])))

lines(years, cSoil_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cVeg_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = cVeg_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = cVeg_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cVeg')

lines(years, cVeg_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = lai_lnd_mean_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = lai_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = lai_lnd_mean_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'Lai')

lines(years, lai_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = rh_lnd_sum_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = rh_lnd_sum_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = rh_lnd_sum_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,  col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'RH')

lines(years, rh_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fLuc_lnd_sum_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = fLuc_lnd_sum_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = fLuc_lnd_sum_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fLuc')

lines(years, fLuc_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fHarvest_lnd_sum_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = fHarvest_lnd_sum_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = fHarvest_lnd_sum_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fHarvest')

lines(years, fHarvest_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = treeFrac_lnd_mean_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = treeFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = treeFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'treefrac'
                 )

lines(years, treeFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = shrubFrac_lnd_mean_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = shrubFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = shrubFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'shrubfrac'
)

lines(years, shrubFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = baresoilFrac_lnd_mean_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = baresoilFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = baresoilFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'baresoilfrac')

lines(years, baresoilFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)


linePlotMultiEns(years = years, c3PftFrac_lnd_mean_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = c3PftFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = c3PftFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'c3PftFrac')

lines(years, c3PftFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)


linePlotMultiEns(years = years, c4PftFrac_lnd_mean_ens_wave00[without_outliers_ix_wave00,],
                 ens2 = c4PftFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = c4PftFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'c4PftFrac')

lines(years, c4PftFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)


reset()

legend('bottomright', legend = c('wave00','wave01','wave01 level2','standard'), lty = 'solid', lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol), inset = c(0.05, 0.15) )

dev.off()
null device 
          1 

pdf(file = 'figs/fig04.pdf', width = 10, height = 12)
#pdf(file = 'figs/carbon-cycle-timeseries-anomaly-waves-constrained.pdf', width = 10, height = 12)
par(mfrow= c(3,5), las = 1, mar = c(4,4,1,0))

linePlotMultiEns(years = years, ens1 = npp_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = npp_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = npp_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NPP')

lines(years,npp_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 =  nbp_ens_anom_wave00[without_outliers_ix_wave00,], 
                 ens2 = nbp_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = nbp_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NBP', ylim = c(-10,10))

lines(years, nbp_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cSoil_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = cSoil_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = cSoil_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cSoil', ylim = range(c(cSoil_ens_anom_wave00[,1], cSoil_ens_anom_wave00[,164])))

lines(years, cSoil_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cVeg_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = cVeg_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = cVeg_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cVeg')

lines(years, cVeg_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = lai_lnd_mean_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = lai_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = lai_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'Lai')

lines(years, lai_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = rh_lnd_sum_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = rh_lnd_sum_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = rh_lnd_sum_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,  col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'RH')

lines(years, rh_lnd_sum_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fLuc_lnd_sum_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = fLuc_lnd_sum_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = fLuc_lnd_sum_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fLuc')

lines(years, fLuc_lnd_sum_stan_anom, col = stancol, lty = 'solid', lwd = 2)
 
linePlotMultiEns(years = years, ens1 = fHarvest_lnd_sum_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = fHarvest_lnd_sum_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = fHarvest_lnd_sum_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fHarvest')

lines(years, fHarvest_lnd_sum_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = treeFrac_lnd_mean_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = treeFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = treeFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'treefrac'
                 )

lines(years, treeFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = shrubFrac_lnd_mean_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = shrubFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = shrubFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'shrubfrac'
)

lines(years, shrubFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = baresoilFrac_lnd_mean_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = baresoilFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = baresoilFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'baresoilfrac')

lines(years, baresoilFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)


linePlotMultiEns(years = years, c3PftFrac_lnd_mean_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = c3PftFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = c3PftFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'c3PftFrac')

lines(years, c3PftFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)


linePlotMultiEns(years = years, c4PftFrac_lnd_mean_ens_anom_wave00[without_outliers_ix_wave00,],
                 ens2 = c4PftFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
                 ens3 = c4PftFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = '%', main = 'c4PftFrac')

lines(years, c4PftFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)

reset()

legend('bottomright', legend = c('wave00','wave01','wave01 level2','standard'), lty = 'solid', lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol), inset = c(0.05, 0.15) )


dev.off()
null device 
          1 

1.3 Exploring summary of constraint: Cumulative NBP

1.3.1 Carbon budget data

Section 2.5 in Friedlingstein et al. describes how the land carbon sink is estimated.


#pdf(file = 'carbon_budget.pdf', width = 10, height = 8)
# Question: How closely should our model match this curve? Which output?
# (My guess is 'Total Land Carbon anomaly')
historical_carbon_budget <- read_excel('data/Global_Carbon_Budget_2020v1.0.xlsx', sheet = "Historical Budget", skip = 15, n_max = 270)

par(mfrow = c(3,2))
ylim = c(-1, 6)


land_sink_net <- historical_carbon_budget$`land sink` - historical_carbon_budget$`land-use change emissions`

plot(historical_carbon_budget$Year, historical_carbon_budget$`fossil emissions excluding carbonation`, main = 'fossil emissions excluding carbonation', ylab = '',
     type = 'l', bty = 'n', ylim = ylim)

plot(historical_carbon_budget$Year, historical_carbon_budget$`land sink`, main = 'land sink', ylab = '', type = 'l', bty = 'n', ylim = ylim)

lines(historical_carbon_budget$Year, land_sink_net, col = 'red')

legend('topleft', legend = 'land sink - land use change emissions', col = 'red', lty = 'solid')

plot(historical_carbon_budget$Year, historical_carbon_budget$`land-use change emissions`, main = 'land use change emissions',
     ylab = '', type = 'l', bty = 'n', ylim = ylim)

plot(historical_carbon_budget$Year, historical_carbon_budget$`atmospheric growth`, main = 'atmospheric growth', ylab = '',
     type = 'l', bty = 'n', ylim = ylim)


plot(historical_carbon_budget$Year, historical_carbon_budget$`ocean sink`, main = 'ocean sink', type = 'l', bty = 'n', ylim = ylim)

plot(historical_carbon_budget$Year, historical_carbon_budget$`budget imbalance`, main = 'budget imbalance', ylab = '', type = 'l', bty = 'n', ylim = ylim)

#dev.off()

IPCC AR6 Chapter 5 states: “The net land carbon sink is taken as net biome productivity (NBP) and so includes any modelled net land-use change emissions. Further details on data sources and processing are available in the chapter data table (Table 5.SM.6).”

The GCP says: “The land sink is the average of several dynamic global vegetation models that reproduce the observed mean total land sink of the 1990s.”

AR6 Chapter 5 states: "The land carbon cycle components of historical ESM simulations show a larger range, with simulated cumulative land carbon uptake (1850–2014) spanning the range from –47 to +21 GtC, compared to the GCP estimate of –12 ± 50 GtC (Figure 5.23b).


#pdf(file = 'carbon_budget.pdf', width = 10, height = 8)
# Question: How closely should our model match this curve? Which output?
# (My guess is 'Total Land Carbon anomaly')
historical_carbon_budget <- read_excel('data/Global_Carbon_Budget_2020v1.0.xlsx', sheet = "Historical Budget", skip = 15, n_max = 270)

par(mfrow = c(3,2))
ylim = c(-1, 6)


land_sink_net <- historical_carbon_budget$`land sink` - historical_carbon_budget$`land-use change emissions`

plot(historical_carbon_budget$Year, historical_carbon_budget$`fossil emissions excluding carbonation`, main = 'fossil emissions excluding carbonation', ylab = '',
     type = 'l', bty = 'n', ylim = ylim)

plot(historical_carbon_budget$Year, historical_carbon_budget$`land sink`, main = 'land sink', ylab = '', type = 'l', bty = 'n', ylim = ylim)

lines(historical_carbon_budget$Year, land_sink_net, col = 'red')

legend('topleft', legend = 'land sink - land use change emissions', col = 'red', lty = 'solid')

plot(historical_carbon_budget$Year, historical_carbon_budget$`land-use change emissions`, main = 'land use change emissions',
     ylab = '', type = 'l', bty = 'n', ylim = ylim)

plot(historical_carbon_budget$Year, historical_carbon_budget$`atmospheric growth`, main = 'atmospheric growth', ylab = '',
     type = 'l', bty = 'n', ylim = ylim)


plot(historical_carbon_budget$Year, historical_carbon_budget$`ocean sink`, main = 'ocean sink', type = 'l', bty = 'n', ylim = ylim)

plot(historical_carbon_budget$Year, historical_carbon_budget$`budget imbalance`, main = 'budget imbalance', ylab = '', type = 'l', bty = 'n', ylim = ylim)


#dev.off()


plot(historical_carbon_budget$Year, historical_carbon_budget$`land sink`,
     main = 'land sink', ylab = '', type = 'l', bty = 'n', ylim = ylim)



plot(historical_carbon_budget$Year, cumsum(historical_carbon_budget$`land sink`),
     main = 'land sink', ylab = '', type = 'l', bty = 'n')


#plot(historical_carbon_budget$Year, cumsum(land_sink_net, na.rm = TRUE),
#     main = 'land sink', ylab = '', type = 'l', bty = 'n')

match_years_ix <- which(historical_carbon_budget$Year %in% 1850:2013)

match_years <- historical_carbon_budget$Year[match_years_ix]
cumulative_net_land_sink <- cumsum(land_sink_net[match_years_ix])


par(las = 1)

plot(match_years, cumulative_net_land_sink,
     type = 'l', main = "Cumulative Net Land Sink", xlab = "", ylab = "GtC",
     ylim = c(-80, 20))

2 How much are all select output constrained?


par(mar = c(8,4,4,1), las = 1)
cnbp_ens_wave00 <- t(apply(nbp_ens_wave00, 1, FUN = cumsum))
cnbp_ens_wave01 <- t(apply(nbp_ens_wave01, 1, FUN = cumsum))
cnbp_stan <- cumsum(nbp_stan)


linePlotMultiEns(years = years, ens1 =  cnbp_ens_wave00[without_outliers_ix_wave00,], 
                 ens2 = cnbp_ens_wave01[without_outliers_ix_wave01,],
                 ens3 = cnbp_ens_wave01[level2a_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
                 ylab = 'Cumulative NBP (GtC)', main = 'Cumulative NBP', xlim = c(1850, 2035))

lines(years, cnbp_stan, col = 'black', lwd = 2)

lines(match_years, cumulative_net_land_sink, lty = 'dashed', col = 'black')

arrows(2022,-47, 2022,21, angle = 90, length = 0.05, code = 3)

arrows(2035,(-12-50), 2035,(-12+50),  angle = 90, length = 0.05, code = 3)

text(2022, 30, 'AR6', cex = 0.8)

text(2035, 47, 'GCP', cex = 0.8)
reset()


legend('bottom', legend = c('wave00','wave01','wave01 level2','standard', 'GCP estimate'), lty = c('solid', 'solid', 'solid', 'solid', 'dashed'), lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol, stancol), horiz = FALSE, inset = 0.01, cex = 0.8)

NA
NA

selected_tags <- c('npp', 'nbp', 'cSoil', 'cVeg', 'lai_lnd_mean', 'rh_lnd_sum', 'fLuc_lnd_sum', 'fHarvest_lnd_sum', 'treeFrac_lnd_mean', 'shrubFrac_lnd_mean', 'baresoilFrac_lnd_mean', 'c3PftFrac_lnd_mean', 'c4PftFrac_lnd_mean')

# wave00
selected_tags_vec_wave00 <- paste0(selected_tags, '_ens_wave00')

mv_means_wave00 <- matrix(ncol = length(selected_tags), nrow = nrow(npp_ens_wave00))
colnames(mv_means_wave00) <- selected_tags

for(i in 1:length(selected_tags_vec_wave00)){
  
  dat <- get(selected_tags_vec_wave00[i])
  
  colix <- 1:ncol(dat)
  trunc_dat <- dat[, tail(colix,20)]
  
  mean_dat <- apply(trunc_dat, 1, mean, na.rm = TRUE)
  mv_means_wave00[,i ] <- mean_dat
  
}

## wave01
selected_tags_vec_wave01 <- paste0(selected_tags, '_ens_wave01')
mv_means_wave01 <- matrix(ncol = length(selected_tags), nrow = nrow(npp_ens_wave01))
colnames(mv_means_wave01) <- selected_tags

for(i in 1:length(selected_tags_vec_wave01)){
  
  dat <- get(selected_tags_vec_wave01[i])
  
  colix <- 1:ncol(dat)
  trunc_dat <- dat[, tail(colix,20)]
  
  mean_dat <- apply(trunc_dat, 1, mean, na.rm = TRUE)
  mv_means_wave01[,i ] <- mean_dat
  
}


# wave00 anomaly
selected_tags_vec_anom_wave00 <- paste0(selected_tags, '_ens_anom_wave00')

mv_means_anom_wave00 <- matrix(ncol = length(selected_tags), nrow = nrow(npp_ens_anom_wave00))
colnames(mv_means_anom_wave00) <- selected_tags

for(i in 1:length(selected_tags_vec_anom_wave00)){
  
  dat <- get(selected_tags_vec_anom_wave00[i])
  
  colix <- 1:ncol(dat)
  trunc_dat <- dat[, tail(colix,20)]
  
  mean_dat <- apply(trunc_dat, 1, mean, na.rm = TRUE)
  mv_means_anom_wave00[,i ] <- mean_dat
  
}

# wave01 anomaly
selected_tags_vec_anom_wave01 <- paste0(selected_tags, '_ens_anom_wave01')

mv_means_anom_wave01 <- matrix(ncol = length(selected_tags), nrow = nrow(npp_ens_anom_wave01))
colnames(mv_means_anom_wave01) <- selected_tags

for(i in 1:length(selected_tags_vec_anom_wave01)){
  
  dat <- get(selected_tags_vec_anom_wave01[i])
  
  colix <- 1:ncol(dat)
  trunc_dat <- dat[, tail(colix,20)]
  
  mean_dat <- apply(trunc_dat, 1, mean, na.rm = TRUE)
  mv_means_anom_wave01[,i ] <- mean_dat
  
}

range_wave00 <- apply(mv_means_wave00[without_outliers_ix_wave00,], 2 , range) # Wave00 sets the initial range
range_wave01 <- apply(mv_means_wave01[without_outliers_ix_wave01,], 2, range)
range_wave01_level2a <- apply(mv_means_wave01[level2a_ix_wave01, ], 2, range)

# What is the output range of the level2a modern value, expressed as a proportion of the initial ensemble?
range_prop_wave01_level2a <-  (apply(range_wave01_level2a, 2, diff) / apply(range_wave00, 2, diff)) *100


range_anom_wave00 <- apply(mv_means_anom_wave00[without_outliers_ix_wave00,], 2 , range) # Wave00 sets the initial range
range_anom_wave01 <- apply(mv_means_anom_wave01[without_outliers_ix_wave01,], 2, range)
range_anom_wave01_level2a <- apply(mv_means_anom_wave01[level2a_ix_wave01, ], 2, range)

# What is the output range of the level2a modern value, expressed as a proportion of the initial ensemble?
range_prop_anom_wave01_level2a <-  (apply(range_anom_wave01_level2a, 2, diff) / apply(range_anom_wave00, 2, diff)) *100

2.0.1 Constraint of output at level 2

Proportion of the initial range of model output that is covered by level 2 constrained ensemble (%):

all_waves_mv <- rbind(mv_means_wave00[without_outliers_ix_wave00,], mv_means_wave01[without_outliers_ix_wave01,])

range_all_waves <- apply(all_waves_mv, 2, range)

level2_ix_all_waves_mv <- which(all_waves_mv[,'nbp'] > 0 &
                                  all_waves_mv[,'npp'] > 35 &
                                  all_waves_mv[,'npp'] < 80 &
                                  all_waves_mv[,'cSoil'] > 750 &
                                  all_waves_mv[,'cSoil'] < 3000 &
                                  all_waves_mv[,'cVeg'] > 300 &
                                  all_waves_mv[,'cVeg'] < 800
)

all_waves_mv_level2 <- all_waves_mv[level2_ix_all_waves_mv, ]


all_waves_col <- rep(zred, nrow(all_waves_mv))

all_waves_level2_col <- all_waves_col
all_waves_level2_col[level2_ix_all_waves_mv] <- zblue

range_all_waves_level2 <- apply(all_waves_mv[level2_ix_all_waves_mv, ], 2, range)

range_prop_all_waves_level2 <-(apply(range_all_waves_level2, 2, diff) / apply(range_all_waves, 2, diff)) *100

# Anomaly
all_waves_anom_mv <- rbind(mv_means_anom_wave00[without_outliers_ix_wave00,], mv_means_anom_wave01[without_outliers_ix_wave01,])

range_all_waves_anom <- apply(all_waves_anom_mv, 2, range)

all_waves_anom_mv_level2 <- all_waves_anom_mv[level2_ix_all_waves_mv, ]

range_all_waves_anom_level2 <- apply(all_waves_anom_mv[level2_ix_all_waves_mv, ], 2, range)

range_prop_all_waves_anom_level2 <-(apply(range_all_waves_anom_level2, 2, diff) / apply(range_all_waves_anom, 2, diff)) *100
print(round(range_prop_all_waves_level2,1))
                  npp                   nbp                 cSoil                  cVeg          lai_lnd_mean            rh_lnd_sum 
                 26.6                  55.1                  48.3                  23.5                  60.9                  26.0 
         fLuc_lnd_sum      fHarvest_lnd_sum     treeFrac_lnd_mean    shrubFrac_lnd_mean baresoilFrac_lnd_mean    c3PftFrac_lnd_mean 
                 51.5                  60.7                  88.3                  85.4                  52.9                  58.0 
   c4PftFrac_lnd_mean 
                 43.9 
                                

pdf(file = "figs/induced_constraint_barplot.pdf")
range_prop_round = round(range_prop_all_waves_level2,1)
range_prop_round_mat <- rbind(range_prop_round, (100 - range_prop_round))

range_prop_anom_round = round(range_prop_all_waves_anom_level2,1)
range_prop_anom_round_mat <- rbind(range_prop_anom_round, (100 - range_prop_anom_round))

par(las = 1, mar = c(8,1,3,2), mfrow = c(1,2), oma = c(1,10, 1,1))
b1 <- barplot(t(apply(range_prop_round_mat,1,rev)), 
        horiz = TRUE,
        col =  c( zblue, zred),
        xlab = 'Ensemble output range (%)',
        main = 'Absolute'
        )
text(0, rev(b1), range_prop_round, pos = 4, cex = 0.9)

b2 <- barplot(t(apply(range_prop_anom_round_mat,1,rev)), 
        horiz = TRUE,
        col =  c( zblue, zred),
        xlab = 'Ensemble output range (%)',
        names.arg = rep('', 13),
        main = 'Anomaly'
        )
text(0, rev(b2), range_prop_anom_round, pos = 4, cex = 0.9)

axis(1)
reset()
legend('bottom', legend = c('NROY', 'Ruled out'), fill = c(zblue, zred), horiz = TRUE, inset = 0.02 )
dev.off()
null device 
          1 

2.0.2 Pairs plot of 2d projections of constrained output space.



axat = length(range_prop_round):1

par(las = 1, mar = c(5,10,3,2), xaxs = 'i')
plot(rev(range_prop_round), axat, xlim = c(0,100), axes = FALSE, type = 'n', ylab = '', xlab = 'Level 2 NROY proportion of whole range (%)')
#abline(h = axat, col = 'grey', lty = 'dashed')
#abline(v = 100)
points(rev(range_prop_anom_round), rev(axat), col = 'grey', pch = 19)
segments(x0 = 0, y0 = rev(axat), x1 = rev(range_prop_anom_round), y1 = rev(axat), col = 'grey', lty = 'dashed')


points(rev(range_prop_round), rev(axat), col = 'black', pch = 19)
segments(x0 = 0, y0 = rev(axat), x1 = rev(range_prop_round), y1 = rev(axat))
#segments(x0 = 0, y0 = rev(axat), x1 = rev(range_prop_anom_round), y1 = rev(axat), col = 'grey', lty = 'dashed')

axis(2, at  = axat, labels = selected_tags)
axis(1)
reset()
legend('top', c('Absolute', 'Anomaly'), pch = 19, col = c('black', 'grey'), horiz = TRUE, inset = 0.03)

NA
NA

2.1 Constraining to level 2 with the emulator

#pdf(file = 'figs/output_pairs.pdf', width = 10, height = 10)
pairs(all_waves_mv, col = all_waves_level2_col, lower.panel = NULL, gap = 0,
      pch = 20,
      labels = 1:13
      )
reset()
legend('topleft', legend = c('NROY', 'Ruled out' ), pch = 20, col = c(zblue, zred), inset = c(0.03, 0.25))
legend('left', legend = c(paste(1:13, colnames(all_waves_mv))), inset = 0.03, cex = 1)

#dev.off()

# Can this go in common data? Would be needed for checking emulator fits
# Create fit lists for the combined data wave00 level 1a and wave01
Y_const_level1a_wave01_scaled_list <- mat2list(Y_const_level1a_wave01_scaled)

fit_list_const_level1a_wave01 <- mclapply(X = Y_const_level1a_wave01_scaled_list , FUN = km, formula = ~., design = X_level1a_wave01,
                                   mc.cores = 4, control = list(trace = FALSE))

Y_const_level1a_wave01_scaled_pred <- multiPred(Y = Y_const_level1a_wave01_scaled, Xpred = X_unif, fit_list = fit_list_const_level1a_wave01)


level2_ix_em_unif_wave00_wave01 <- which(Y_const_level1a_wave01_scaled_pred$pred_mean[,'nbp_lnd_sum'] > 0 &
                                           Y_const_level1a_wave01_scaled_pred$pred_mean[,'npp_nlim_lnd_sum'] > 35 & 
                                           Y_const_level1a_wave01_scaled_pred$pred_mean[,'npp_nlim_lnd_sum'] < 80 &
                                           Y_const_level1a_wave01_scaled_pred$pred_mean[,'cSoil_lnd_sum'] > 750 &
                                           Y_const_level1a_wave01_scaled_pred$pred_mean[,'cSoil_lnd_sum'] < 3000 &
                                           Y_const_level1a_wave01_scaled_pred$pred_mean[,'cVeg_lnd_sum'] > 300 & 
                                           Y_const_level1a_wave01_scaled_pred$pred_mean[,'cVeg_lnd_sum'] < 800
)


(length(level2_ix_em_unif_wave00_wave01) / nunif) * 100
X_stan_norm <- normalize(matrix(rep(1, 32), nrow = 1), wrt = lhs)

colnames(X_unif) <- 1:32

pdf(file = 'figs/fig08.pdf', width = 12, height = 12)
#pdf(file = 'figs/pairs_level2_ix_em_unif_wave00_wave01.pdf', width = 12, height = 12)

par(oma = c(0,0,0,3), bg = 'white')

panel_hist_local <- function(x, ...)
{
    usr <- par("usr"); on.exit(par(usr))
    par(usr = c(usr[1:2], 0, 1.5) )
    h <- hist(x, plot = FALSE)
    breaks <- h$breaks; nB <- length(breaks)
    y <- h$counts; y <- y/max(y)
    rect(breaks[-nB], 0, breaks[-1], y, col = "cyan", ...)
}

pairs(rbind(X_unif[level2_ix_em_unif_wave00_wave01, ], X_stan_norm),
      gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
      panel = dfunc_up_truth,
      diag.panel = panel_hist_local,
      cex.labels = 1,
      col.axis = 'white',
      dfunc_col = rb
      )


image.plot(legend.only = TRUE,
           zlim = c(0,1),
           col = rb,
           legend.args = list(text = 'Density of model runs matching the criteria', side = 3, line = 1),
           legend.shrink = 0.6,
           horizontal = TRUE
)

legend('left', legend = paste(1:32, colnames(lhs)), cex = 1, bty = 'n')

dev.off()
LS0tCnRpdGxlOiAiQ29uc3RyYWluaW5nIHRoZSBjYXJib24gY3ljbGUgaW4gSlVMRVMtRVMtMS4wIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICcyJwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCgpDb2RlIHRvIHJ1biBhbmFseXNpcyBhbmQgZ2VuZXJhdGUgZmlndXJlcyBmb3IgTWNOZWFsbCBldCBhbC4gKDIwMjMpICJDb25zdHJhaW5pbmcgdGhlIGNhcmJvbiBjeWNsZSBpbiBKVUxFUy1FUy0xLjAiCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CiMgTG9hZCBoZWxwZXIgZnVuY3Rpb25zCgprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLnBhdGggPSAiZmlncy8iLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZ3MgPSBGQUxTRSkKCgpgYGAKCmBgYHtyfQoKc291cmNlKCJKVUxFUy1FUy0xcDAtY29tbW9uLXBhY2thZ2VzLlIiKQpzb3VyY2UoIkpVTEVTLUVTLTFwMC1jb21tb24tZnVuY3Rpb25zLlIiKQpzb3VyY2UoIkpVTEVTLUVTLTFwMC1jb21tb24tZGF0YS5SIikKCmBgYAoKIyMgV2hhdCBhcmUgdGhlIGRhdGE/CmBgYHtyfQpnZXRTdGFuZGFyZE1lbWJlckxvbmdOYW1lIDwtIGZ1bmN0aW9uKGVuc2xvYywgdmFyaWFibGUpewogICMgSGVscGVyIGZ1bmN0aW9uIHRvIGdldCB0aGUgbG9uZyBuYW1lIG9mIG91dHB1dCB2YXJpYWJsZXMgKGFuZCB0aGUgdW5pdHMpCiAgCiAgZW5zbWVtYmVyIDwtICdTMycKICBmbiA8LSBwYXN0ZTAoZW5zbG9jLCdKVUxFUy1FUy0xcDBfJyxlbnNtZW1iZXIsJ19Bbm51YWxfZ2xvYmFsLm5jJykKCiAgbmMgPC0gbmNfb3BlbihwYXN0ZTAoZm4pKQogIGxuIDwtIG5jJHZhcltbdmFyaWFibGVdXSRsb25nbmFtZQogIHVuIDwtIG5jJHZhcltbdmFyaWFibGVdXSR1bml0cyAgICAgIAogIAogIHJldHVybihsaXN0KGxvbmduYW1lID0gbG4sIHVuaXRzID0gdW4pKQogIAp9CgojIGZvcihpIGluIHlfbmFtZXNfc2VsZWN0KXsKIyAgIAojICAgb3V0IDwtIGdldFN0YW5kYXJkTWVtYmVyTG9uZ05hbWUoZW5zbG9jX3dhdmUwMCwgaSkKIyAgIHByaW50KGkpCiMgICBwcmludChvdXQpCiMgfQoKYGBgCgoKCiMjIEZhaWx1cmUgYW5hbHlzaXMKCmBgYHtyIGZhaWx1cmUtcGFpcnMsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMiwgZmlnLnBhdGg9J2ZpZ3MvJywgZGV2PWMoJ3BuZycsICdwZGYnKX0KCgpsb3dfbnBwX2l4IDwtIHdoaWNoKFlbLCducHBfbmxpbV9sbmRfc3VtJ10gPCAxZTUpCiMgY29kZSBmcm9tIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI4MTgyODcyL2hvdy10by11c2UtZGlmZmVyZW50LXNldHMtb2YtZGF0YS1pbi1sb3dlci1hbmQtdXBwZXItcGFuZWwtb2YtcGFpcnMtZnVuY3Rpb24taW4KCgojWCA8LSBtYXRyaXgocnVuaWYoMzAwKSwgbmNvbD0zKQojWSA8LSBtYXRyaXgoYyhzb3J0KHJ1bmlmKDEwMCwgMCwgMTApKSwgCiMgICAgICAgICAgICAgIHNvcnQocnVuaWYoMTAwLCAwLCAxMCkpLCAKIyAgICAgICAgICAgICAgc29ydChydW5pZigxMDAsIDAsIDEwKSkpLCBuY29sPTMpCgpwZGYoZmlsZSA9ICdmaWdzL2ZpZzAyLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEwKQojcGRmKGZpbGUgPSAnZmlncy9ydW4tZmFpbHVyZS1wYWlycy5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMCkKeDEgPC0gWFtsb3dfbnBwX2l4LCBdCngyIDwtIFhfbmxldmVsMAoKWFkgPC0gcmJpbmQoeDEsIHgyKQoKCnBhaXJzKFhZLAogICAgICBsb3dlci5wYW5lbD1mdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgICBYeCA8LSB4W3NlcV9sZW4obnJvdyh4MSkpXSAjIGNvcnJlc3BvbmRzIHRvIFggc3Vic2V0CiAgICAgICAgWHkgPC0geVtzZXFfbGVuKG5yb3coeDEpKV0gIyBjb3JyZXNwb25kcyB0byBYIHN1YnNldAogICAgICAgICN1c3IgPC0gcGFyKCJ1c3IiKTsgb24uZXhpdChwYXIodXNyKSkKICAgICAgICAjcGFyKHVzciA9IGMocmFuZ2UoeDFbLCAtbmNvbCh4MSldKSwgcmFuZ2UoeDFbLCAtMV0pKSkgIyBzZXQgdXAgbGltaXRzCiAgICAgICAgcG9pbnRzKFh4LCBYeSwgY29sID0gemJsdWUsIHBjaCA9IDE5LCBjZXggPSAwLjgpCiAgICAgICAjIGlmKHBhcignbWZnJylbMl0gPT0gMSkgYXhpcygyKSAjIGlmIGxlZnQgcGxvdCwgYWRkIGxlZnQgYXhpcwogICAgICAgICNpZihwYXIoJ21mZycpWzFdID09IG5jb2woeDEpKSBheGlzKDEpICMgaWYgYm90dG9tIHBsb3QgYWRkIGJvdHRvbSBheGlzCiAgICAgIH0sIAogICAgICB1cHBlci5wYW5lbD1mdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgICBZeCA8LSB4Wyhucm93KHgxKSArIDEpOmxlbmd0aCh4KV0gIyBZIHN1YnNldAogICAgICAgIFl5IDwtIHlbKG5yb3coeDEpICsgMSk6bGVuZ3RoKHkpXSAjIFkgc3Vic2V0CiAgICAgICAgCiAgICAgICAgI2NudHIgPC0gb3V0ZXIoWXgsIFl4LCBGVU49JyonKSAjIGFyYml0cmFyeSBmdW5jdGlvbiBmb3IgY29udG91cgogICAgICAgIyB1c3IgPC0gcGFyKCJ1c3IiKTsgb24uZXhpdChwYXIodXNyKSkKICAgICAgICAjcGFyKHVzciA9IGMocmFuZ2UoeDJbLCAtMV0pLCByYW5nZSh4MlssIC1uY29sKHgyKV0pKSkgIyBzZXQgdXAgbGltaXRzCiAgICAgICAgcG9pbnRzKFl4LCBZeSwgY29sID0genJlZCwgcGNoID0gMTksIGNleCA9IDAuOCkKICAgICAgICAjY29udG91cihZeCwgWXksIGNudHIsIGFkZD1UUlVFKQogICAgICAgICNpZihwYXIoJ21mZycpWzJdID09IG5jb2woeDIpKSBheGlzKDQpICMgaWYgcmlnaHQgcGxvdCwgYWRkIHJpZ2h0IGF4aXMKICAgICAgICAjaWYocGFyKCdtZmcnKVsxXSA9PSAxKSBheGlzKDMpICMgaWYgdG9wIHBsb3QsIGFkZCB0b3AgYXhpcwogICAgICB9LCAKICAgICAgI3RpY2s9RkFMU0UsICMgc3VwcHJlc3MgdGhlIGRlZmF1bHQgdGljayBtYXJrcwogICAgICAjbGluZT0xLAogICAgICBnYXAgPSAwLAogICAgICB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBsYWJlbHMgPSAxOmQsCiAgICAgIG9tYSA9IGMoMiwgMTgsIDIsIDIpKSAjIG1vdmUgdGhlIGRlZmF1bHQgdGljayBsYWJlbHMgb2ZmIHRoZSBwbG90IAoKcmVzZXQoKQoKbGVnZW5kKCdsZWZ0JywgbGVnZW5kID0gcGFzdGUoMTpkLCBjb2xuYW1lcyhsaHMpKSwgY2V4ID0gMS4xLCBidHkgPSAnbicpCmxlZ2VuZCgndG9wbGVmdCcsIHBjaCA9IDE5LCBjb2wgPSBjKCB6cmVkLCB6Ymx1ZSksIGxlZ2VuZCA9IGMoJ2ZhaWxlZCcsICd6ZXJvIGNhcmJvbiBjeWNsZScpLCBidHkgPSAnbicsIGluc2V0ID0gMC4wMiwgY2V4ID0gMS4xICkKCmRldi5vZmYoKQoKYGBgCgoKIyMgRmFpbHVyZSBhbmFseXNpcyBtYXJnaW5hbCBwbG90cyAKCkFyZSB0aGVyZSBoYXJkIHRocmVzaG9sZHMgYWZ0ZXIgd2hpY2ggdGhlIG1vZGVsIGFsd2F5cyBmYWlscz8gKFF1ZXN0aW9uIGZyb20gcmV2aWV3IGNvbW1lbnRzKS4KUnVuIGZhaWx1cmVzIGFyZSBpbiB0CgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0KCgp5X2xldmVsMCA8LSBZX2xldmVsMFssJ25wcF9ubGltX2xuZF9zdW0nXQoKcGRmKGZpbGUgPSAnZmlncy9tYXJnaW5hbF9mYWlsdXJlcy5wZGYnLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCkKcGFyKG1mcm93ID0gYyg1LDcpLCBtYXIgPSBjKDMsMSwzLDEpLCBvbWEgPSBjKDEsMSw1LDEpKQpmb3IoaSBpbiAxOnApewogIHBsb3QoWF9sZXZlbDBbLGldLCB5X2xldmVsMCwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSBjb2xuYW1lcyhYX2xldmVsMClbaV0sIHhsaW0gPSBjKDAsMSkpCiAgcG9pbnRzKFhfbmxldmVsMFssaV0sIHJlcCgwLCBucm93KFhfbmxldmVsMCkpLCBjb2wgPSB6cmVkLCBwY2ggPSAxOSkKICBwb2ludHMoeDFbLCBpIF0sIHJlcCgwLG5yb3coeDEpKSwgY29sID0gemJsdWUsIHBjaCA9IDE5KQogIAp9Cm10ZXh0KCdOUFAnLCBvdXRlciA9IFRSVUUsIHNpZGUgPSAzLCBjZXggPSAyLCBsaW5lID0gMikKcmVzZXQoKQpsZWdlbmQoJ2JvdHRvbXJpZ2h0JywgcGNoID0gMTksIGNvbCA9IGMoJ2JsYWNrJywgenJlZCwgemJsdWUpLCBsZWdlbmQgPSBjKCdyYW4nLCdmYWlsZWQnLCAnemVybyBjYXJib24gY3ljbGUnKSwgYnR5ID0gJ24nLCBpbnNldCA9IDAuMDUsIGNleCA9IDEuMSApCmRldi5vZmYoKQoKYGBgCgogIyMgSXMgdGhlcmUgYSBwYXR0ZXJuIHRvIHRoZSB3YXZlMDEgdGltZXNlcmllcyBvdXRsaWVycz8KYGBge3IsIGZpZy53aWR0aD0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KCnBkZihmaWxlID0gJ2ZpZ3MvWF93YXZlMDFfdGltZXNlcmllc19vdXRsaWVycy5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKcGFpcnMoWF93YXZlMDFfdHJhaW5bdHNfb3V0bGllcnNfaXhfd2F2ZTAxLCBdLAogICAgICBnYXAgPSAwLCAKICAgICAgeGxpbSA9IGMoMCwxKSwKICAgICAgeWxpbSA9IGMoMCwxKQopCmRldi5vZmYoKQogICAgICAKICAgICAgCgoKYGBgCgoKCgojIFZpc3VhbGlzaW5nIHRoZSBlbnNlbWJsZSByYW5nZQoKSXQncyBpbXBvcnRhbnQgdG8gcmVtZW1iZXIgdGhhdCB0aGUgZGVzaWduIG9mIHRoZSBleHBlcmltZW50IGlzIG11bHRpcGxpY2F0aW9uIGZhY3RvcnMgb2YgdGhlIG9yaWdpbmFsIHBhcmFtZXRlcnMuIFRoaXMgbWlnaHQgYmUgaW1wb3J0YW50IGZvciB0aGUgImhvbGQiIHZhbHVlIGluIGEgc2Vuc2l0aXZpdHkgYW5hbHlzaXMsIGFzIHRoZSAic3RhbmRhcmQiIHZhbHVlIGFuZCB0aGUgbWVkaWFuIHZhbHVlIG9mIHRoZSBlbnNlbWJsZSB3aWxsIG5vdCBiZSB0aGUgc2FtZS4KCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gOH0KCiNwZGYoZmlsZSA9ICdmaWdzL2xoc19yYW5nZS5wZGYnLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgpCnBkZihmaWxlID0gJ2ZpZ3MvZmlnMDEucGRmJywgd2lkdGggPSA2LCBoZWlnaHQgPSA4KQpwYXIobGFzID0gMSwgbWFyID0gYyg1LDgsMiwxKSkKbGhzX21pbiA8LSBhcHBseShsaHNfd2F2ZTBfd2F2ZTAxX2FsbCwgMiwgbWluKQpsaHNfbWF4IDwtIGFwcGx5KGxoc193YXZlMF93YXZlMDFfYWxsLDIsIG1heCkKCnBsb3QobGhzX21heCwgMTpkLCB0eXBlID0gJ24nLCB4bGltID0gYygwLDEwKSwgYXhlcyA9IEZBTFNFLCB4bGFiID0gJ211bHRpcGx5aW5nIGZhY3RvcicsIHlsYWIgPSAnJykKCmFibGluZSh2ID0gMCwgbHR5ID0gJ2Rhc2hlZCcsIGNvbCA9ICdncmV5JykKYWJsaW5lKHYgPSAxLCBsdHkgPSAnZGFzaGVkJywgY29sID0gJ3RvbWF0bzInKQoKc2VnbWVudHMoeDAgPSBsaHNfbWluLCB5MCA9IDE6ZCwgeDEgPSBsaHNfbWF4LCB5MSA9IDE6ZCApCnBvaW50cyhsaHNfbWluLCAxOmQsIHBjaCA9IDIwKQpwb2ludHMobGhzX21heCwgMTpkLCBwY2ggPSAyMCkKYXhpcygyLCBhdCA9IDE6ZCwgbGFiZWxzID0gY29sbmFtZXMobGhzKSkKYXhpcygxKQpkZXYub2ZmKCkKCmBgYAoKIyMgV2F2ZTAwL1dhdmUwMSAgRW5zZW1ibGUgYmVoYXZpb3VyIGluIGtleSAoY29uc3RyYWluaW5nKSBvdXRwdXRzLiAKCkdsb2JhbCBtZWFuIGZvciB0aGUgMjAgeWVhcnMgYXQgdGhlIGVuZCBvZiB0aGUgMjB0aCBDZW50dXJ5LiBUaGVyZSBpcyBzdGlsbCBhIHNpZ25pZmljYW50IGxvdyBiaWFzIG9uIGNWZWcgb3V0cHV0LgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4fQp3YXZlMDBjb2wgPC0gJ3NreWJsdWUyJwp3YXZlMDFjb2wgPC0gJ3RvbWF0bzInCgp3YXZlMDBjb2wgPC0gJ2RvZGdlcmJsdWUyJwp3YXZlMDFjb2wgPC0gJ2ZpcmVicmljaycKcmFuZ2Vjb2wgPC0gJ2dyZXknCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCB9CiMgSGlzdG9ncmFtIG9mIGxldmVsIDEgY29uc3RyYWludHMKaGNvbCA9ICdkYXJrZ3JleScKbGNvbCA9ICdibGFjaycKCnBkZihmaWxlID0gJ2ZpZ3MvZmlnMDUucGRmJywgd2lkdGggPSA4LCBoZWlnaHQgPSA4KQojcGRmKGZpbGUgPSAnZmlncy9sZXZlbF8yX2NvbnN0cmFpbnRzX2hpc3RzLnBkZicsIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCkKcGFyKG1mcm93ID0gYygyLDIpLCBmZyA9ICdkYXJrZ3JleScsIGxhcyA9IDEsIG9tYSA9IGMoMC4xLCAwLjEsIDQsIDAuMSkpCgp0cnVuYyA8LSBmdW5jdGlvbih4LCB2ZWMpewogIAogIGRhdCA8LSB4W3ggPCBtYXgodmVjKSAmIHggPiBtaW4odmVjKSAgXQogIAogIGRhdAogIAp9CgoKaCA8LSBoaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCduYnBfbG5kX3N1bSddLCBtYWluID0gJ05CUCcsIHhsYWIgPSAnR3RDL3llYXInLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCkpCmhpc3QodHJ1bmMoWV9jb25zdF93YXZlMDFfc2NhbGVkIFssJ25icF9sbmRfc3VtJ10sIGgkYnJlYWtzKSAsCiAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwxNTApICwgYnJlYWtzID0gaCRicmVha3MsIGFkZCA9IFRSVUUpCgpydWcoWV9jb25zdF9zdGFuX3NjYWxlZFsnbmJwX2xuZF9zdW0nXSwgbHdkID0gMikKCnBvbHlnb24oeCA9IGMoMCwgMTAwLCAxMDAsIDApLCB5ID0gYygwLCAwLCAxMDAwLCAxMDAwKSwKICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSwKICAgICAgICBib3JkZXIgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wpKQoKaCA8LSBoaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCducHBfbmxpbV9sbmRfc3VtJ10sY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApLCBtYWluID0gJ05QUCcsIHhsYWIgPSAnR3RDL3llYXInKQpoaXN0KHRydW5jKFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCBbLCducHBfbmxpbV9sbmRfc3VtJ10sIGgkYnJlYWtzKSAsIAogICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wpICwgYnJlYWtzID0gaCRicmVha3MsIGFkZCA9IFRSVUUpCgpydWcoWV9jb25zdF9zdGFuX3NjYWxlZFsnbnBwX25saW1fbG5kX3N1bSddLCBsd2QgPSAyKQoKcG9seWdvbih4ID0gYygzNSwgODAsIDgwLCAzNSksIHkgPSBjKDAsIDAsIDEwMDAsIDEwMDApLAogICAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApLAogICAgICAgIGJvcmRlciA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCkpCgoKaCA8LSBoaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCdjU29pbF9sbmRfc3VtJ10sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwgbWFpbiA9ICdTb2lsIENhcmJvbicsIHhsYWIgPSAnR3RDJykKaGlzdCh0cnVuYyhZX2NvbnN0X3dhdmUwMV9zY2FsZWQgWywnY1NvaWxfbG5kX3N1bSddLCBoJGJyZWFrcykgLCAKICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDE1MCkgLCBicmVha3MgPSBoJGJyZWFrcywgYWRkID0gVFJVRSkKCnJ1ZyhZX2NvbnN0X3N0YW5fc2NhbGVkWydjU29pbF9sbmRfc3VtJ10sIGx3ZCA9IDIpCgpwb2x5Z29uKHggPSBjKDc1MCwgMzAwMCwgMzAwMCwgNzUwKSwgeSA9IGMoMCwgMCwgMTAwMCwgMTAwMCksCiAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sLCA2MCksCiAgICAgICAgYm9yZGVyID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sKSkKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnY1ZlZ19sbmRfc3VtJ10sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwgbWFpbiA9ICdWZWdldGF0aW9uIENhcmJvbicsIHhsYWIgPSAnR3RDJykKaGlzdCh0cnVuYyhZX2NvbnN0X3dhdmUwMV9zY2FsZWQgWywnY1ZlZ19sbmRfc3VtJ10sIGgkYnJlYWtzKSAsIAogICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDE1MCkgICwgYnJlYWtzID0gaCRicmVha3MsIGFkZCA9IFRSVUUpCgpydWcoWV9jb25zdF9zdGFuX3NjYWxlZFsnY1ZlZ19sbmRfc3VtJ10sIGx3ZCA9IDIpCgpwb2x5Z29uKHggPSBjKDMwMCwgODAwLCA4MDAsIDMwMCksIHkgPSBjKDAsIDAsIDEwMDAsIDEwMDApLAogICAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApLAogICAgICAgYm9yZGVyID0gIG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCkpCgoKCnJlc2V0KCkKCmxlZ2VuZCgndG9wJywgaG9yaXogPSBUUlVFLCBmaWxsID0gYyhtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxNTApLCBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAxNTApLCBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSksIGxlZ2VuZCA9IGMoJ1dhdmUwMCcsICdXYXZlMDEnLCAnQVcgcmFuZ2UnKSkKCmRldi5vZmYoKQpgYGAKCiMjIFdoYXQgcHJvcG9ydGlvbiBvZiB3YXZlMDEgZmFsbCB3aXRoaW4gQW5keSBXaWx0c2hpcmUncyBjb25zdHJhaW50cz8KCkp1c3QgdW5kZXIgYSB0aGlyZC4gUG9pbnRzIGF0IGEgc2lnbmlmaWNhbnQgbW9kZWwgZGlzY3JlcGVuY3kgaW4gY1ZlZwoKT2YgdGhlIDQwMCBtZW1iZXJzIG9mIHRoZSB3YXZlMDEgZW5zZW1ibGUsIDEyOCBwYXNzIEFuZHkgV2lsdHNoaXJlJ3MgTGV2ZWwgMiBjb25zdHJhaW50cy4KCmBgYHtyfQoKbGVuZ3RoKGxldmVsMl9peF93YXZlMDEpCmxlbmd0aChsZXZlbDJfaXhfd2F2ZTAxKSAvIG50cmFpbl93YXZlMDEKCgpgYGAKCgoKYGBge3IgcGxvdC1jYXJib24tY3ljbGUtdGltZXNlcmllcy1wcmltYXJ5LCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQoKbGNvbF93YXZlMCA8LSBtYWtlVHJhbnNwYXJlbnQoJ2RvZGdlcmJsdWUyJywgIDEyMCkKbGNvbF93YXZlMDEgPC0gbWFrZVRyYW5zcGFyZW50KCdmaXJlYnJpY2snLCAgMTIwKQpsY29sX3dhdmUwMV9sZXZlbDIgPC0gJ2dvbGQnCnN0YW5jb2wgPSAnYmxhY2snCgpsaW5lUGxvdE11bHRpRW5zIDwtIGZ1bmN0aW9uKHllYXJzLCBlbnMxLCBlbnMyLCBlbnMzLCBjb2wxLCBjb2wyLCBjb2wzLCB5bGFiLCBtYWluLCB5bGltID0gTlVMTCwgLi4uKXsKICAjIFBsb3Qgd2F2ZTAwIGFuZCB3YXZlMDEgdGltZXNlcmllcyBvbiB0b3Agb2Ygb25lIGFub3RoZXIKICAKICBudCA8LSBsZW5ndGgoeWVhcnMpIAogIGlmKGlzLm51bGwoeWxpbSkpewogICAgCiAgeWxpbSA9IHJhbmdlKGMoZW5zMVssMV0sIGVuczFbLG50XSwgZW5zMlssMV0sIGVuczJbICxudF0sIGVuczNbLDFdLCBlbnMzWywgbnRdKSkKICB9CiAgCiAgZWxzZSB5bGltIDwtIHlsaW0KICAKICBtYXRwbG90KHllYXJzLCB0KGVuczEpLCB0eXBlID0gJ2wnLCBsdHkgPSAnc29saWQnLHlsaW0gPSB5bGltLCBjb2wgPSBjb2wxLAogICAgICAgIHlsYWIgPSB5bGFiLCBtYWluID0gbWFpbiwgeGxhYiA9ICcnLAogICAgICAgIGJ0eSA9ICduJywgLi4uKQogIG1hdGxpbmVzKHllYXJzLCB0KGVuczIpLCBjb2wgPSBjb2wyLCBsdHkgPSAnc29saWQnKQogICAgbWF0bGluZXMoeWVhcnMsIHQoZW5zMyksIGNvbCA9IGNvbDMsIGx0eSA9ICdzb2xpZCcpCn0KCnBkZihmaWxlID0gJ2ZpZ3MvZmlnMDMucGRmJywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIpCiNwZGYoZmlsZSA9ICdmaWdzL2NhcmJvbi1jeWNsZS10aW1lc2VyaWVzLXdhdmVzLWNvbnN0cmFpbmVkLnBkZicsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEyKQpwYXIobWZyb3c9IGMoMyw1KSwgbGFzID0gMSwgbWFyID0gYyg0LDQsMSwwKSkKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IG5wcF9lbnNfd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IG5wcF9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IG5wcF9lbnNfd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ05QUCcpCgpsaW5lcyh5ZWFycyxucHBfc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9ICBuYnBfZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLCAKICAgICAgICAgICAgICAgICBlbnMyID0gbmJwX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gbmJwX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSxjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdOQlAnLCB5bGltID0gYygtMTAsMTApKQoKbGluZXMoeWVhcnMsIG5icF9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gY1NvaWxfZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBjU29pbF9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGNTb2lsX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnY1NvaWwnLCB5bGltID0gcmFuZ2UoYyhjU29pbF9lbnNfd2F2ZTAwWywxXSwgY1NvaWxfZW5zX3dhdmUwMFssMTY0XSkpKQoKbGluZXMoeWVhcnMsIGNTb2lsX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBjVmVnX2Vuc193YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gY1ZlZ19lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGNWZWdfZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjVmVnJykKCmxpbmVzKHllYXJzLCBjVmVnX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdMYWknKQoKbGluZXMoeWVhcnMsIGxhaV9sbmRfbWVhbl9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gcmhfbG5kX3N1bV9lbnNfd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHJoX2xuZF9zdW1fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSByaF9sbmRfc3VtX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ1JIJykKCmxpbmVzKHllYXJzLCByaF9sbmRfc3VtX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBmTHVjX2xuZF9zdW1fZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBmTHVjX2xuZF9zdW1fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBmTHVjX2xuZF9zdW1fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdmTHVjJykKCmxpbmVzKHllYXJzLCBmTHVjX2xuZF9zdW1fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBmSGFydmVzdF9sbmRfc3VtX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2ZIYXJ2ZXN0JykKCmxpbmVzKHllYXJzLCBmSGFydmVzdF9sbmRfc3VtX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSB0cmVlRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHRyZWVGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAndHJlZWZyYWMnCiAgICAgICAgICAgICAgICAgKQoKbGluZXMoeWVhcnMsIHRyZWVGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAnc2hydWJmcmFjJwopCgpsaW5lcyh5ZWFycywgc2hydWJGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAnYmFyZXNvaWxmcmFjJykKCmxpbmVzKHllYXJzLCBiYXJlc29pbEZyYWNfbG5kX21lYW5fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICclJywgbWFpbiA9ICdjM1BmdEZyYWMnKQoKbGluZXMoeWVhcnMsIGMzUGZ0RnJhY19sbmRfbWVhbl9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ2M0UGZ0RnJhYycpCgpsaW5lcyh5ZWFycywgYzRQZnRGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgoKcmVzZXQoKQoKbGVnZW5kKCdib3R0b21yaWdodCcsIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsJ3dhdmUwMScsJ3dhdmUwMSBsZXZlbDInLCdzdGFuZGFyZCcpLCBsdHkgPSAnc29saWQnLCBsd2QgPSAxLjUsIGNvbCA9IGMobGNvbF93YXZlMCwgbGNvbF93YXZlMDEsIGxjb2xfd2F2ZTAxX2xldmVsMiwgc3RhbmNvbCksIGluc2V0ID0gYygwLjA1LCAwLjE1KSApCgpkZXYub2ZmKCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0KCnBkZihmaWxlID0gJ2ZpZ3MvZmlnMDQucGRmJywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIpCiNwZGYoZmlsZSA9ICdmaWdzL2NhcmJvbi1jeWNsZS10aW1lc2VyaWVzLWFub21hbHktd2F2ZXMtY29uc3RyYWluZWQucGRmJywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdz0gYygzLDUpLCBsYXMgPSAxLCBtYXIgPSBjKDQsNCwxLDApKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gbnBwX2Vuc19hbm9tX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBucHBfZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IG5wcF9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnTlBQJykKCmxpbmVzKHllYXJzLG5wcF9zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9ICBuYnBfZW5zX2Fub21fd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sIAogICAgICAgICAgICAgICAgIGVuczIgPSBuYnBfZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IG5icF9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSxjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdOQlAnLCB5bGltID0gYygtMTAsMTApKQoKbGluZXMoeWVhcnMsIG5icF9zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBjU29pbF9lbnNfYW5vbV93YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gY1NvaWxfZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGNTb2lsX2Vuc19hbm9tX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjU29pbCcsIHlsaW0gPSByYW5nZShjKGNTb2lsX2Vuc19hbm9tX3dhdmUwMFssMV0sIGNTb2lsX2Vuc19hbm9tX3dhdmUwMFssMTY0XSkpKQoKbGluZXMoeWVhcnMsIGNTb2lsX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGNWZWdfZW5zX2Fub21fd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IGNWZWdfZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGNWZWdfZW5zX2Fub21fd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2NWZWcnKQoKbGluZXMoeWVhcnMsIGNWZWdfc3Rhbl9hbm9tLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gbGFpX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBsYWlfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGxhaV9sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnTGFpJykKCmxpbmVzKHllYXJzLCBsYWlfbG5kX21lYW5fc3Rhbl9hbm9tLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gcmhfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gcmhfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gcmhfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ1JIJykKCmxpbmVzKHllYXJzLCByaF9sbmRfc3VtX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGZMdWNfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gZkx1Y19sbmRfc3VtX2Vuc19hbm9tX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBmTHVjX2xuZF9zdW1fZW5zX2Fub21fd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2ZMdWMnKQoKbGluZXMoeWVhcnMsIGZMdWNfbG5kX3N1bV9zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCiAKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnZkhhcnZlc3QnKQoKbGluZXMoeWVhcnMsIGZIYXJ2ZXN0X2xuZF9zdW1fc3Rhbl9hbm9tLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHRyZWVGcmFjX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSB0cmVlRnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ3RyZWVmcmFjJwogICAgICAgICAgICAgICAgICkKCmxpbmVzKHllYXJzLCB0cmVlRnJhY19sbmRfbWVhbl9zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gc2hydWJGcmFjX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAnc2hydWJmcmFjJwopCgpsaW5lcyh5ZWFycywgc2hydWJGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICclJywgbWFpbiA9ICdiYXJlc29pbGZyYWMnKQoKbGluZXMoeWVhcnMsIGJhcmVzb2lsRnJhY19sbmRfbWVhbl9zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBjM1BmdEZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gYzNQZnRGcmFjX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAnYzNQZnRGcmFjJykKCmxpbmVzKHllYXJzLCBjM1BmdEZyYWNfbG5kX21lYW5fc3Rhbl9hbm9tLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBjNFBmdEZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGM0UGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ2M0UGZ0RnJhYycpCgpsaW5lcyh5ZWFycywgYzRQZnRGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCnJlc2V0KCkKCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCd3YXZlMDEnLCd3YXZlMDEgbGV2ZWwyJywnc3RhbmRhcmQnKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMS41LCBjb2wgPSBjKGxjb2xfd2F2ZTAsIGxjb2xfd2F2ZTAxLCBsY29sX3dhdmUwMV9sZXZlbDIsIHN0YW5jb2wpLCBpbnNldCA9IGMoMC4wNSwgMC4xNSkgKQoKCmRldi5vZmYoKQoKYGBgIAoKIyMgRXhwbG9yaW5nIHN1bW1hcnkgb2YgY29uc3RyYWludDogQ3VtdWxhdGl2ZSBOQlAKCgojIyMgQ2FyYm9uIGJ1ZGdldCBkYXRhCgpbU2VjdGlvbiAyLjUgaW4gRnJpZWRsaW5nc3RlaW4gZXQgYWwuXShodHRwczovL2Vzc2QuY29wZXJuaWN1cy5vcmcvYXJ0aWNsZXMvMTIvMzI2OS8yMDIwLyNzZWN0aW9uMikgZGVzY3JpYmVzIGhvdyB0aGUgbGFuZCBjYXJib24gc2luayBpcyBlc3RpbWF0ZWQuCmBgYHtyLCBoaXN0b3JpY2FsLWNhcmJvbi1idWRnZXQsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0KCiNwZGYoZmlsZSA9ICdjYXJib25fYnVkZ2V0LnBkZicsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCiMgUXVlc3Rpb246IEhvdyBjbG9zZWx5IHNob3VsZCBvdXIgbW9kZWwgbWF0Y2ggdGhpcyBjdXJ2ZT8gV2hpY2ggb3V0cHV0PwojIChNeSBndWVzcyBpcyAnVG90YWwgTGFuZCBDYXJib24gYW5vbWFseScpCmhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCA8LSByZWFkX2V4Y2VsKCdkYXRhL0dsb2JhbF9DYXJib25fQnVkZ2V0XzIwMjB2MS4wLnhsc3gnLCBzaGVldCA9ICJIaXN0b3JpY2FsIEJ1ZGdldCIsIHNraXAgPSAxNSwgbl9tYXggPSAyNzApCgpwYXIobWZyb3cgPSBjKDMsMikpCnlsaW0gPSBjKC0xLCA2KQoKCmxhbmRfc2lua19uZXQgPC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBsYW5kIHNpbmtgIC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBsYW5kLXVzZSBjaGFuZ2UgZW1pc3Npb25zYAoKcGxvdChoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBmb3NzaWwgZW1pc3Npb25zIGV4Y2x1ZGluZyBjYXJib25hdGlvbmAsIG1haW4gPSAnZm9zc2lsIGVtaXNzaW9ucyBleGNsdWRpbmcgY2FyYm9uYXRpb24nLCB5bGFiID0gJycsCiAgICAgdHlwZSA9ICdsJywgYnR5ID0gJ24nLCB5bGltID0geWxpbSkKCnBsb3QoaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIsIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgbGFuZCBzaW5rYCwgbWFpbiA9ICdsYW5kIHNpbmsnLCB5bGFiID0gJycsIHR5cGUgPSAnbCcsIGJ0eSA9ICduJywgeWxpbSA9IHlsaW0pCgpsaW5lcyhoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgbGFuZF9zaW5rX25ldCwgY29sID0gJ3JlZCcpCgpsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSAnbGFuZCBzaW5rIC0gbGFuZCB1c2UgY2hhbmdlIGVtaXNzaW9ucycsIGNvbCA9ICdyZWQnLCBsdHkgPSAnc29saWQnKQoKcGxvdChoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBsYW5kLXVzZSBjaGFuZ2UgZW1pc3Npb25zYCwgbWFpbiA9ICdsYW5kIHVzZSBjaGFuZ2UgZW1pc3Npb25zJywKICAgICB5bGFiID0gJycsIHR5cGUgPSAnbCcsIGJ0eSA9ICduJywgeWxpbSA9IHlsaW0pCgpwbG90KGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyLCBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGF0bW9zcGhlcmljIGdyb3d0aGAsIG1haW4gPSAnYXRtb3NwaGVyaWMgZ3Jvd3RoJywgeWxhYiA9ICcnLAogICAgIHR5cGUgPSAnbCcsIGJ0eSA9ICduJywgeWxpbSA9IHlsaW0pCgoKcGxvdChoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBvY2VhbiBzaW5rYCwgbWFpbiA9ICdvY2VhbiBzaW5rJywgdHlwZSA9ICdsJywgYnR5ID0gJ24nLCB5bGltID0geWxpbSkKCnBsb3QoaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIsIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgYnVkZ2V0IGltYmFsYW5jZWAsIG1haW4gPSAnYnVkZ2V0IGltYmFsYW5jZScsIHlsYWIgPSAnJywgdHlwZSA9ICdsJywgYnR5ID0gJ24nLCB5bGltID0geWxpbSkKCiNkZXYub2ZmKCkKYGBgCklQQ0MgQVI2IENoYXB0ZXIgNSBzdGF0ZXM6ICJUaGUgbmV0IGxhbmQgY2FyYm9uIHNpbmsgaXMgdGFrZW4gYXMgbmV0IGJpb21lIHByb2R1Y3Rpdml0eSAoTkJQKSBhbmQgc28gaW5jbHVkZXMgYW55IG1vZGVsbGVkIG5ldCBsYW5kLXVzZSAKY2hhbmdlIGVtaXNzaW9ucy4gRnVydGhlciBkZXRhaWxzIG9uIGRhdGEgc291cmNlcyBhbmQgcHJvY2Vzc2luZyBhcmUgYXZhaWxhYmxlIGluIHRoZSBjaGFwdGVyIGRhdGEgdGFibGUgKFRhYmxlIDUuU00uNikuIgoKVGhlIEdDUCBzYXlzOiAKIlRoZSBsYW5kIHNpbmsgaXMgdGhlIGF2ZXJhZ2Ugb2Ygc2V2ZXJhbCBkeW5hbWljIGdsb2JhbCB2ZWdldGF0aW9uIG1vZGVscyB0aGF0IHJlcHJvZHVjZSB0aGUgb2JzZXJ2ZWQgbWVhbiB0b3RhbCBsYW5kIHNpbmsgb2YgdGhlIDE5OTBzLiIKCkFSNiBDaGFwdGVyIDUgc3RhdGVzOiAgIlRoZSBsYW5kIGNhcmJvbiBjeWNsZSBjb21wb25lbnRzIG9mIGhpc3RvcmljYWwgRVNNIHNpbXVsYXRpb25zIHNob3cgYSBsYXJnZXIgcmFuZ2UsIHdpdGggc2ltdWxhdGVkIGN1bXVsYXRpdmUgbGFuZCBjYXJib24gdXB0YWtlICgxODUw4oCTMjAxNCkgc3Bhbm5pbmcgdGhlIHJhbmdlIGZyb20g4oCTNDcgdG8gKzIxIEd0QywgY29tcGFyZWQgdG8gdGhlIEdDUCBlc3RpbWF0ZSBvZiDigJMxMiDCsSA1MCBHdEMgKEZpZ3VyZSA1LjIzYikuCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodCA9IDd9CgoKcGxvdChoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBsYW5kIHNpbmtgLAogICAgIG1haW4gPSAnbGFuZCBzaW5rJywgeWxhYiA9ICcnLCB0eXBlID0gJ2wnLCBidHkgPSAnbicsIHlsaW0gPSB5bGltKQoKCnBsb3QoaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIsIGN1bXN1bShoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGxhbmQgc2lua2ApLAogICAgIG1haW4gPSAnbGFuZCBzaW5rJywgeWxhYiA9ICcnLCB0eXBlID0gJ2wnLCBidHkgPSAnbicpCgoKI3Bsb3QoaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIsIGN1bXN1bShsYW5kX3NpbmtfbmV0LCBuYS5ybSA9IFRSVUUpLAojICAgICBtYWluID0gJ2xhbmQgc2luaycsIHlsYWIgPSAnJywgdHlwZSA9ICdsJywgYnR5ID0gJ24nKQoKbWF0Y2hfeWVhcnNfaXggPC0gd2hpY2goaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIgJWluJSAxODUwOjIwMTMpCgptYXRjaF95ZWFycyA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhclttYXRjaF95ZWFyc19peF0KY3VtdWxhdGl2ZV9uZXRfbGFuZF9zaW5rIDwtIGN1bXN1bShsYW5kX3NpbmtfbmV0W21hdGNoX3llYXJzX2l4XSkKCgpwYXIobGFzID0gMSkKcGxvdChtYXRjaF95ZWFycywgY3VtdWxhdGl2ZV9uZXRfbGFuZF9zaW5rLAogICAgIHR5cGUgPSAnbCcsIG1haW4gPSAiQ3VtdWxhdGl2ZSBOZXQgTGFuZCBTaW5rIiwgeGxhYiA9ICIiLCB5bGFiID0gIkd0QyIsCiAgICAgeWxpbSA9IGMoLTgwLCAyMCkpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gOH0KCnBhcihtYXIgPSBjKDgsNCw0LDEpLCBsYXMgPSAxKQpjbmJwX2Vuc193YXZlMDAgPC0gdChhcHBseShuYnBfZW5zX3dhdmUwMCwgMSwgRlVOID0gY3Vtc3VtKSkKY25icF9lbnNfd2F2ZTAxIDwtIHQoYXBwbHkobmJwX2Vuc193YXZlMDEsIDEsIEZVTiA9IGN1bXN1bSkpCmNuYnBfc3RhbiA8LSBjdW1zdW0obmJwX3N0YW4pCgoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gIGNuYnBfZW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLCAKICAgICAgICAgICAgICAgICBlbnMyID0gY25icF9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGNuYnBfZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdDdW11bGF0aXZlIE5CUCAoR3RDKScsIG1haW4gPSAnQ3VtdWxhdGl2ZSBOQlAnLCB4bGltID0gYygxODUwLCAyMDM1KSkKCmxpbmVzKHllYXJzLCBjbmJwX3N0YW4sIGNvbCA9ICdibGFjaycsIGx3ZCA9IDIpCgpsaW5lcyhtYXRjaF95ZWFycywgY3VtdWxhdGl2ZV9uZXRfbGFuZF9zaW5rLCBsdHkgPSAnZGFzaGVkJywgY29sID0gJ2JsYWNrJykKCmFycm93cygyMDIyLC00NywgMjAyMiwyMSwgYW5nbGUgPSA5MCwgbGVuZ3RoID0gMC4wNSwgY29kZSA9IDMpCgphcnJvd3MoMjAzNSwoLTEyLTUwKSwgMjAzNSwoLTEyKzUwKSwgIGFuZ2xlID0gOTAsIGxlbmd0aCA9IDAuMDUsIGNvZGUgPSAzKQoKdGV4dCgyMDIyLCAzMCwgJ0FSNicsIGNleCA9IDAuOCkKCnRleHQoMjAzNSwgNDcsICdHQ1AnLCBjZXggPSAwLjgpCnJlc2V0KCkKCgpsZWdlbmQoJ2JvdHRvbScsIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsJ3dhdmUwMScsJ3dhdmUwMSBsZXZlbDInLCdzdGFuZGFyZCcsICdHQ1AgZXN0aW1hdGUnKSwgbHR5ID0gYygnc29saWQnLCAnc29saWQnLCAnc29saWQnLCAnc29saWQnLCAnZGFzaGVkJyksIGx3ZCA9IDEuNSwgY29sID0gYyhsY29sX3dhdmUwLCBsY29sX3dhdmUwMSwgbGNvbF93YXZlMDFfbGV2ZWwyLCBzdGFuY29sLCBzdGFuY29sKSwgaG9yaXogPSBGQUxTRSwgaW5zZXQgPSAwLjAxLCBjZXggPSAwLjgpCgoKYGBgCgoKCgojIEhvdyBtdWNoIGFyZSBhbGwgc2VsZWN0IG91dHB1dCBjb25zdHJhaW5lZD8KYGBge3J9CgpzZWxlY3RlZF90YWdzIDwtIGMoJ25wcCcsICduYnAnLCAnY1NvaWwnLCAnY1ZlZycsICdsYWlfbG5kX21lYW4nLCAncmhfbG5kX3N1bScsICdmTHVjX2xuZF9zdW0nLCAnZkhhcnZlc3RfbG5kX3N1bScsICd0cmVlRnJhY19sbmRfbWVhbicsICdzaHJ1YkZyYWNfbG5kX21lYW4nLCAnYmFyZXNvaWxGcmFjX2xuZF9tZWFuJywgJ2MzUGZ0RnJhY19sbmRfbWVhbicsICdjNFBmdEZyYWNfbG5kX21lYW4nKQoKIyB3YXZlMDAKc2VsZWN0ZWRfdGFnc192ZWNfd2F2ZTAwIDwtIHBhc3RlMChzZWxlY3RlZF90YWdzLCAnX2Vuc193YXZlMDAnKQoKbXZfbWVhbnNfd2F2ZTAwIDwtIG1hdHJpeChuY29sID0gbGVuZ3RoKHNlbGVjdGVkX3RhZ3MpLCBucm93ID0gbnJvdyhucHBfZW5zX3dhdmUwMCkpCmNvbG5hbWVzKG12X21lYW5zX3dhdmUwMCkgPC0gc2VsZWN0ZWRfdGFncwoKZm9yKGkgaW4gMTpsZW5ndGgoc2VsZWN0ZWRfdGFnc192ZWNfd2F2ZTAwKSl7CiAgCiAgZGF0IDwtIGdldChzZWxlY3RlZF90YWdzX3ZlY193YXZlMDBbaV0pCiAgCiAgY29saXggPC0gMTpuY29sKGRhdCkKICB0cnVuY19kYXQgPC0gZGF0WywgdGFpbChjb2xpeCwyMCldCiAgCiAgbWVhbl9kYXQgPC0gYXBwbHkodHJ1bmNfZGF0LCAxLCBtZWFuLCBuYS5ybSA9IFRSVUUpCiAgbXZfbWVhbnNfd2F2ZTAwWyxpIF0gPC0gbWVhbl9kYXQKICAKfQoKIyMgd2F2ZTAxCnNlbGVjdGVkX3RhZ3NfdmVjX3dhdmUwMSA8LSBwYXN0ZTAoc2VsZWN0ZWRfdGFncywgJ19lbnNfd2F2ZTAxJykKbXZfbWVhbnNfd2F2ZTAxIDwtIG1hdHJpeChuY29sID0gbGVuZ3RoKHNlbGVjdGVkX3RhZ3MpLCBucm93ID0gbnJvdyhucHBfZW5zX3dhdmUwMSkpCmNvbG5hbWVzKG12X21lYW5zX3dhdmUwMSkgPC0gc2VsZWN0ZWRfdGFncwoKZm9yKGkgaW4gMTpsZW5ndGgoc2VsZWN0ZWRfdGFnc192ZWNfd2F2ZTAxKSl7CiAgCiAgZGF0IDwtIGdldChzZWxlY3RlZF90YWdzX3ZlY193YXZlMDFbaV0pCiAgCiAgY29saXggPC0gMTpuY29sKGRhdCkKICB0cnVuY19kYXQgPC0gZGF0WywgdGFpbChjb2xpeCwyMCldCiAgCiAgbWVhbl9kYXQgPC0gYXBwbHkodHJ1bmNfZGF0LCAxLCBtZWFuLCBuYS5ybSA9IFRSVUUpCiAgbXZfbWVhbnNfd2F2ZTAxWyxpIF0gPC0gbWVhbl9kYXQKICAKfQoKCiMgd2F2ZTAwIGFub21hbHkKc2VsZWN0ZWRfdGFnc192ZWNfYW5vbV93YXZlMDAgPC0gcGFzdGUwKHNlbGVjdGVkX3RhZ3MsICdfZW5zX2Fub21fd2F2ZTAwJykKCm12X21lYW5zX2Fub21fd2F2ZTAwIDwtIG1hdHJpeChuY29sID0gbGVuZ3RoKHNlbGVjdGVkX3RhZ3MpLCBucm93ID0gbnJvdyhucHBfZW5zX2Fub21fd2F2ZTAwKSkKY29sbmFtZXMobXZfbWVhbnNfYW5vbV93YXZlMDApIDwtIHNlbGVjdGVkX3RhZ3MKCmZvcihpIGluIDE6bGVuZ3RoKHNlbGVjdGVkX3RhZ3NfdmVjX2Fub21fd2F2ZTAwKSl7CiAgCiAgZGF0IDwtIGdldChzZWxlY3RlZF90YWdzX3ZlY19hbm9tX3dhdmUwMFtpXSkKICAKICBjb2xpeCA8LSAxOm5jb2woZGF0KQogIHRydW5jX2RhdCA8LSBkYXRbLCB0YWlsKGNvbGl4LDIwKV0KICAKICBtZWFuX2RhdCA8LSBhcHBseSh0cnVuY19kYXQsIDEsIG1lYW4sIG5hLnJtID0gVFJVRSkKICBtdl9tZWFuc19hbm9tX3dhdmUwMFssaSBdIDwtIG1lYW5fZGF0CiAgCn0KCiMgd2F2ZTAxIGFub21hbHkKc2VsZWN0ZWRfdGFnc192ZWNfYW5vbV93YXZlMDEgPC0gcGFzdGUwKHNlbGVjdGVkX3RhZ3MsICdfZW5zX2Fub21fd2F2ZTAxJykKCm12X21lYW5zX2Fub21fd2F2ZTAxIDwtIG1hdHJpeChuY29sID0gbGVuZ3RoKHNlbGVjdGVkX3RhZ3MpLCBucm93ID0gbnJvdyhucHBfZW5zX2Fub21fd2F2ZTAxKSkKY29sbmFtZXMobXZfbWVhbnNfYW5vbV93YXZlMDEpIDwtIHNlbGVjdGVkX3RhZ3MKCmZvcihpIGluIDE6bGVuZ3RoKHNlbGVjdGVkX3RhZ3NfdmVjX2Fub21fd2F2ZTAxKSl7CiAgCiAgZGF0IDwtIGdldChzZWxlY3RlZF90YWdzX3ZlY19hbm9tX3dhdmUwMVtpXSkKICAKICBjb2xpeCA8LSAxOm5jb2woZGF0KQogIHRydW5jX2RhdCA8LSBkYXRbLCB0YWlsKGNvbGl4LDIwKV0KICAKICBtZWFuX2RhdCA8LSBhcHBseSh0cnVuY19kYXQsIDEsIG1lYW4sIG5hLnJtID0gVFJVRSkKICBtdl9tZWFuc19hbm9tX3dhdmUwMVssaSBdIDwtIG1lYW5fZGF0CiAgCn0KCmBgYAoKYGBge3J9CgpyYW5nZV93YXZlMDAgPC0gYXBwbHkobXZfbWVhbnNfd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sIDIgLCByYW5nZSkgIyBXYXZlMDAgc2V0cyB0aGUgaW5pdGlhbCByYW5nZQpyYW5nZV93YXZlMDEgPC0gYXBwbHkobXZfbWVhbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sIDIsIHJhbmdlKQpyYW5nZV93YXZlMDFfbGV2ZWwyYSA8LSBhcHBseShtdl9tZWFuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sIDIsIHJhbmdlKQoKIyBXaGF0IGlzIHRoZSBvdXRwdXQgcmFuZ2Ugb2YgdGhlIGxldmVsMmEgbW9kZXJuIHZhbHVlLCBleHByZXNzZWQgYXMgYSBwcm9wb3J0aW9uIG9mIHRoZSBpbml0aWFsIGVuc2VtYmxlPwpyYW5nZV9wcm9wX3dhdmUwMV9sZXZlbDJhIDwtICAoYXBwbHkocmFuZ2Vfd2F2ZTAxX2xldmVsMmEsIDIsIGRpZmYpIC8gYXBwbHkocmFuZ2Vfd2F2ZTAwLCAyLCBkaWZmKSkgKjEwMAoKCnJhbmdlX2Fub21fd2F2ZTAwIDwtIGFwcGx5KG12X21lYW5zX2Fub21fd2F2ZTAwW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sIDIgLCByYW5nZSkgIyBXYXZlMDAgc2V0cyB0aGUgaW5pdGlhbCByYW5nZQpyYW5nZV9hbm9tX3dhdmUwMSA8LSBhcHBseShtdl9tZWFuc19hbm9tX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLCAyLCByYW5nZSkKcmFuZ2VfYW5vbV93YXZlMDFfbGV2ZWwyYSA8LSBhcHBseShtdl9tZWFuc19hbm9tX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwgMiwgcmFuZ2UpCgojIFdoYXQgaXMgdGhlIG91dHB1dCByYW5nZSBvZiB0aGUgbGV2ZWwyYSBtb2Rlcm4gdmFsdWUsIGV4cHJlc3NlZCBhcyBhIHByb3BvcnRpb24gb2YgdGhlIGluaXRpYWwgZW5zZW1ibGU/CnJhbmdlX3Byb3BfYW5vbV93YXZlMDFfbGV2ZWwyYSA8LSAgKGFwcGx5KHJhbmdlX2Fub21fd2F2ZTAxX2xldmVsMmEsIDIsIGRpZmYpIC8gYXBwbHkocmFuZ2VfYW5vbV93YXZlMDAsIDIsIGRpZmYpKSAqMTAwCgoKYGBgCgpgYGB7cn0KYWxsX3dhdmVzX212IDwtIHJiaW5kKG12X21lYW5zX3dhdmUwMFt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLCBtdl9tZWFuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSkKCnJhbmdlX2FsbF93YXZlcyA8LSBhcHBseShhbGxfd2F2ZXNfbXYsIDIsIHJhbmdlKQoKbGV2ZWwyX2l4X2FsbF93YXZlc19tdiA8LSB3aGljaChhbGxfd2F2ZXNfbXZbLCduYnAnXSA+IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsX3dhdmVzX212WywnbnBwJ10gPiAzNSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxfd2F2ZXNfbXZbLCducHAnXSA8IDgwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbF93YXZlc19tdlssJ2NTb2lsJ10gPiA3NTAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsX3dhdmVzX212WywnY1NvaWwnXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsX3dhdmVzX212WywnY1ZlZyddID4gMzAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbF93YXZlc19tdlssJ2NWZWcnXSA8IDgwMAopCgphbGxfd2F2ZXNfbXZfbGV2ZWwyIDwtIGFsbF93YXZlc19tdltsZXZlbDJfaXhfYWxsX3dhdmVzX212LCBdCgoKYWxsX3dhdmVzX2NvbCA8LSByZXAoenJlZCwgbnJvdyhhbGxfd2F2ZXNfbXYpKQoKYWxsX3dhdmVzX2xldmVsMl9jb2wgPC0gYWxsX3dhdmVzX2NvbAphbGxfd2F2ZXNfbGV2ZWwyX2NvbFtsZXZlbDJfaXhfYWxsX3dhdmVzX212XSA8LSB6Ymx1ZQoKcmFuZ2VfYWxsX3dhdmVzX2xldmVsMiA8LSBhcHBseShhbGxfd2F2ZXNfbXZbbGV2ZWwyX2l4X2FsbF93YXZlc19tdiwgXSwgMiwgcmFuZ2UpCgpyYW5nZV9wcm9wX2FsbF93YXZlc19sZXZlbDIgPC0oYXBwbHkocmFuZ2VfYWxsX3dhdmVzX2xldmVsMiwgMiwgZGlmZikgLyBhcHBseShyYW5nZV9hbGxfd2F2ZXMsIDIsIGRpZmYpKSAqMTAwCgojIEFub21hbHkKYWxsX3dhdmVzX2Fub21fbXYgPC0gcmJpbmQobXZfbWVhbnNfYW5vbV93YXZlMDBbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwgbXZfbWVhbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSkKCnJhbmdlX2FsbF93YXZlc19hbm9tIDwtIGFwcGx5KGFsbF93YXZlc19hbm9tX212LCAyLCByYW5nZSkKCmFsbF93YXZlc19hbm9tX212X2xldmVsMiA8LSBhbGxfd2F2ZXNfYW5vbV9tdltsZXZlbDJfaXhfYWxsX3dhdmVzX212LCBdCgpyYW5nZV9hbGxfd2F2ZXNfYW5vbV9sZXZlbDIgPC0gYXBwbHkoYWxsX3dhdmVzX2Fub21fbXZbbGV2ZWwyX2l4X2FsbF93YXZlc19tdiwgXSwgMiwgcmFuZ2UpCgpyYW5nZV9wcm9wX2FsbF93YXZlc19hbm9tX2xldmVsMiA8LShhcHBseShyYW5nZV9hbGxfd2F2ZXNfYW5vbV9sZXZlbDIsIDIsIGRpZmYpIC8gYXBwbHkocmFuZ2VfYWxsX3dhdmVzX2Fub20sIDIsIGRpZmYpKSAqMTAwCgoKCgpgYGAKCiMjIyBDb25zdHJhaW50IG9mIG91dHB1dCBhdCBsZXZlbCAyClByb3BvcnRpb24gb2YgdGhlIGluaXRpYWwgcmFuZ2Ugb2YgbW9kZWwgb3V0cHV0IHRoYXQgaXMgY292ZXJlZCBieSBsZXZlbCAyIGNvbnN0cmFpbmVkIGVuc2VtYmxlICglKToKYGBge3J9CnByaW50KHJvdW5kKHJhbmdlX3Byb3BfYWxsX3dhdmVzX2xldmVsMiwxKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgoKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOH0KCiNwZGYoZmlsZSA9ICJmaWdzL2luZHVjZWRfY29uc3RyYWludF9iYXJwbG90LnBkZiIpCnBkZihmaWxlID0gImZpZ3MvZmlnMDYucGRmIikKCnJhbmdlX3Byb3Bfcm91bmQgPSByb3VuZChyYW5nZV9wcm9wX2FsbF93YXZlc19sZXZlbDIsMSkKcmFuZ2VfcHJvcF9yb3VuZF9tYXQgPC0gcmJpbmQocmFuZ2VfcHJvcF9yb3VuZCwgKDEwMCAtIHJhbmdlX3Byb3Bfcm91bmQpKQoKcmFuZ2VfcHJvcF9hbm9tX3JvdW5kID0gcm91bmQocmFuZ2VfcHJvcF9hbGxfd2F2ZXNfYW5vbV9sZXZlbDIsMSkKcmFuZ2VfcHJvcF9hbm9tX3JvdW5kX21hdCA8LSByYmluZChyYW5nZV9wcm9wX2Fub21fcm91bmQsICgxMDAgLSByYW5nZV9wcm9wX2Fub21fcm91bmQpKQoKcGFyKGxhcyA9IDEsIG1hciA9IGMoOCwxLDMsMiksIG1mcm93ID0gYygxLDIpLCBvbWEgPSBjKDEsMTAsIDEsMSkpCmIxIDwtIGJhcnBsb3QodChhcHBseShyYW5nZV9wcm9wX3JvdW5kX21hdCwxLHJldikpLCAKICAgICAgICBob3JpeiA9IFRSVUUsCiAgICAgICAgY29sID0gIGMoIHpibHVlLCB6cmVkKSwKICAgICAgICB4bGFiID0gJ0Vuc2VtYmxlIG91dHB1dCByYW5nZSAoJSknLAogICAgICAgIG1haW4gPSAnQWJzb2x1dGUnCiAgICAgICAgKQp0ZXh0KDAsIHJldihiMSksIHJhbmdlX3Byb3Bfcm91bmQsIHBvcyA9IDQsIGNleCA9IDAuOSkKCmIyIDwtIGJhcnBsb3QodChhcHBseShyYW5nZV9wcm9wX2Fub21fcm91bmRfbWF0LDEscmV2KSksIAogICAgICAgIGhvcml6ID0gVFJVRSwKICAgICAgICBjb2wgPSAgYyggemJsdWUsIHpyZWQpLAogICAgICAgIHhsYWIgPSAnRW5zZW1ibGUgb3V0cHV0IHJhbmdlICglKScsCiAgICAgICAgbmFtZXMuYXJnID0gcmVwKCcnLCAxMyksCiAgICAgICAgbWFpbiA9ICdBbm9tYWx5JwogICAgICAgICkKdGV4dCgwLCByZXYoYjIpLCByYW5nZV9wcm9wX2Fub21fcm91bmQsIHBvcyA9IDQsIGNleCA9IDAuOSkKCmF4aXMoMSkKcmVzZXQoKQpsZWdlbmQoJ2JvdHRvbScsIGxlZ2VuZCA9IGMoJ05ST1knLCAnUnVsZWQgb3V0JyksIGZpbGwgPSBjKHpibHVlLCB6cmVkKSwgaG9yaXogPSBUUlVFLCBpbnNldCA9IDAuMDIgKQpkZXYub2ZmKCkKCgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gOH0KCgpheGF0ID0gbGVuZ3RoKHJhbmdlX3Byb3Bfcm91bmQpOjEKCnBhcihsYXMgPSAxLCBtYXIgPSBjKDUsMTAsMywyKSwgeGF4cyA9ICdpJykKcGxvdChyZXYocmFuZ2VfcHJvcF9yb3VuZCksIGF4YXQsIHhsaW0gPSBjKDAsMTAwKSwgYXhlcyA9IEZBTFNFLCB0eXBlID0gJ24nLCB5bGFiID0gJycsIHhsYWIgPSAnTGV2ZWwgMiBOUk9ZIHByb3BvcnRpb24gb2Ygd2hvbGUgcmFuZ2UgKCUpJykKI2FibGluZShoID0gYXhhdCwgY29sID0gJ2dyZXknLCBsdHkgPSAnZGFzaGVkJykKI2FibGluZSh2ID0gMTAwKQpwb2ludHMocmV2KHJhbmdlX3Byb3BfYW5vbV9yb3VuZCksIHJldihheGF0KSwgY29sID0gJ2dyZXknLCBwY2ggPSAxOSkKc2VnbWVudHMoeDAgPSAwLCB5MCA9IHJldihheGF0KSwgeDEgPSByZXYocmFuZ2VfcHJvcF9hbm9tX3JvdW5kKSwgeTEgPSByZXYoYXhhdCksIGNvbCA9ICdncmV5JywgbHR5ID0gJ2Rhc2hlZCcpCgoKcG9pbnRzKHJldihyYW5nZV9wcm9wX3JvdW5kKSwgcmV2KGF4YXQpLCBjb2wgPSAnYmxhY2snLCBwY2ggPSAxOSkKc2VnbWVudHMoeDAgPSAwLCB5MCA9IHJldihheGF0KSwgeDEgPSByZXYocmFuZ2VfcHJvcF9yb3VuZCksIHkxID0gcmV2KGF4YXQpKQojc2VnbWVudHMoeDAgPSAwLCB5MCA9IHJldihheGF0KSwgeDEgPSByZXYocmFuZ2VfcHJvcF9hbm9tX3JvdW5kKSwgeTEgPSByZXYoYXhhdCksIGNvbCA9ICdncmV5JywgbHR5ID0gJ2Rhc2hlZCcpCgpheGlzKDIsIGF0ICA9IGF4YXQsIGxhYmVscyA9IHNlbGVjdGVkX3RhZ3MpCmF4aXMoMSkKcmVzZXQoKQpsZWdlbmQoJ3RvcCcsIGMoJ0Fic29sdXRlJywgJ0Fub21hbHknKSwgcGNoID0gMTksIGNvbCA9IGMoJ2JsYWNrJywgJ2dyZXknKSwgaG9yaXogPSBUUlVFLCBpbnNldCA9IDAuMDMpCgoKYGBgCgoKIyMjIFBhaXJzIHBsb3Qgb2YgMmQgcHJvamVjdGlvbnMgb2YgY29uc3RyYWluZWQgb3V0cHV0IHNwYWNlLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CiNwZGYoZmlsZSA9ICdmaWdzL291dHB1dF9wYWlycy5wZGYnLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCkKcGRmKGZpbGUgPSAnZmlncy9maWcwNy5wZGYnLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCkKcGFpcnMoYWxsX3dhdmVzX212LCBjb2wgPSBhbGxfd2F2ZXNfbGV2ZWwyX2NvbCwgbG93ZXIucGFuZWwgPSBOVUxMLCBnYXAgPSAwLAogICAgICBwY2ggPSAyMCwKICAgICAgbGFiZWxzID0gMToxMwogICAgICApCnJlc2V0KCkKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnTlJPWScsICdSdWxlZCBvdXQnICksIHBjaCA9IDIwLCBjb2wgPSBjKHpibHVlLCB6cmVkKSwgaW5zZXQgPSBjKDAuMDMsIDAuMjUpKQpsZWdlbmQoJ2xlZnQnLCBsZWdlbmQgPSBjKHBhc3RlKDE6MTMsIGNvbG5hbWVzKGFsbF93YXZlc19tdikpKSwgaW5zZXQgPSAwLjAzLCBjZXggPSAxKQpkZXYub2ZmKCkKCgpgYGAKCgoKCiMjIENvbnN0cmFpbmluZyB0byBsZXZlbCAyIHdpdGggdGhlIGVtdWxhdG9yCgoKCmBgYHtyfQpudW5pZiA8LSA1MDAwMApYX3VuaWYgPC0gc2FtcF91bmlmKG51bmlmLCBtaW5zID0gcmVwKDAsMzIpLCBtYXhlcyA9IHJlcCgxLCAzMikpCmNvbG5hbWVzKFhfdW5pZikgPC0gY29sbmFtZXMoWCkKYGBgCgoKCmBgYHtyfQoKIyBDYW4gdGhpcyBnbyBpbiBjb21tb24gZGF0YT8gV291bGQgYmUgbmVlZGVkIGZvciBjaGVja2luZyBlbXVsYXRvciBmaXRzCiMgQ3JlYXRlIGZpdCBsaXN0cyBmb3IgdGhlIGNvbWJpbmVkIGRhdGEgd2F2ZTAwIGxldmVsIDFhIGFuZCB3YXZlMDEKWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfbGlzdCA8LSBtYXQybGlzdChZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZCkKCmZpdF9saXN0X2NvbnN0X2xldmVsMWFfd2F2ZTAxIDwtIG1jbGFwcGx5KFggPSBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9saXN0ICwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhX3dhdmUwMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYy5jb3JlcyA9IDQsIGNvbnRyb2wgPSBsaXN0KHRyYWNlID0gRkFMU0UpKQoKCmBgYAoKCmBgYHtyfQoKWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfcHJlZCA8LSBtdWx0aVByZWQoWSA9IFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkLCBYcHJlZCA9IFhfdW5pZiwgZml0X2xpc3QgPSBmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSkKCmBgYAoKCgpgYGB7cn0KCgpsZXZlbDJfaXhfZW1fdW5pZl93YXZlMDBfd2F2ZTAxIDwtIHdoaWNoKFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkX3ByZWQkcHJlZF9tZWFuWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfcHJlZCRwcmVkX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfcHJlZCRwcmVkX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9wcmVkJHByZWRfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9wcmVkJHByZWRfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfcHJlZCRwcmVkX21lYW5bLCdjVmVnX2xuZF9zdW0nXSA+IDMwMCAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfcHJlZCRwcmVkX21lYW5bLCdjVmVnX2xuZF9zdW0nXSA8IDgwMAopCgoKKGxlbmd0aChsZXZlbDJfaXhfZW1fdW5pZl93YXZlMDBfd2F2ZTAxKSAvIG51bmlmKSAqIDEwMAoKYGBgCgoKYGBge3J9Clhfc3Rhbl9ub3JtIDwtIG5vcm1hbGl6ZShtYXRyaXgocmVwKDEsIDMyKSwgbnJvdyA9IDEpLCB3cnQgPSBsaHMpCgpjb2xuYW1lcyhYX3VuaWYpIDwtIDE6MzIKCmBgYAoKCmBgYHtyLGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTIsIHdhcm5pbmcgPSBGQUxTRX0KCnBkZihmaWxlID0gJ2ZpZ3MvZmlnMDgucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCiNwZGYoZmlsZSA9ICdmaWdzL3BhaXJzX2xldmVsMl9peF9lbV91bmlmX3dhdmUwMF93YXZlMDEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCgpwYXIob21hID0gYygwLDAsMCwzKSwgYmcgPSAnd2hpdGUnKQoKcGFuZWxfaGlzdF9sb2NhbCA8LSBmdW5jdGlvbih4LCAuLi4pCnsKICAgIHVzciA8LSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQogICAgcGFyKHVzciA9IGModXNyWzE6Ml0sIDAsIDEuNSkgKQogICAgaCA8LSBoaXN0KHgsIHBsb3QgPSBGQUxTRSkKICAgIGJyZWFrcyA8LSBoJGJyZWFrczsgbkIgPC0gbGVuZ3RoKGJyZWFrcykKICAgIHkgPC0gaCRjb3VudHM7IHkgPC0geS9tYXgoeSkKICAgIHJlY3QoYnJlYWtzWy1uQl0sIDAsIGJyZWFrc1stMV0sIHksIGNvbCA9ICJjeWFuIiwgLi4uKQp9CgpwYWlycyhyYmluZChYX3VuaWZbbGV2ZWwyX2l4X2VtX3VuaWZfd2F2ZTAwX3dhdmUwMSwgXSwgWF9zdGFuX25vcm0pLAogICAgICBnYXAgPSAwLCBsb3dlci5wYW5lbCA9IE5VTEwsIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBhbmVsID0gZGZ1bmNfdXBfdHJ1dGgsCiAgICAgIGRpYWcucGFuZWwgPSBwYW5lbF9oaXN0X2xvY2FsLAogICAgICBjZXgubGFiZWxzID0gMSwKICAgICAgY29sLmF4aXMgPSAnd2hpdGUnLAogICAgICBkZnVuY19jb2wgPSByYgogICAgICApCgoKaW1hZ2UucGxvdChsZWdlbmQub25seSA9IFRSVUUsCiAgICAgICAgICAgemxpbSA9IGMoMCwxKSwKICAgICAgICAgICBjb2wgPSByYiwKICAgICAgICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICdEZW5zaXR5IG9mIG1vZGVsIHJ1bnMgbWF0Y2hpbmcgdGhlIGNyaXRlcmlhJywgc2lkZSA9IDMsIGxpbmUgPSAxKSwKICAgICAgICAgICBsZWdlbmQuc2hyaW5rID0gMC42LAogICAgICAgICAgIGhvcml6b250YWwgPSBUUlVFCikKCmxlZ2VuZCgnbGVmdCcsIGxlZ2VuZCA9IHBhc3RlKDE6MzIsIGNvbG5hbWVzKGxocykpLCBjZXggPSAxLCBidHkgPSAnbicpCgpkZXYub2ZmKCkKYGBgCgoKCgo=